我已经阅读了Jonathan Oliver关于处理乱序事件的好文章。
http://blog.jonathanoliver.com/cqrs-out-of-sequence-messages-and-read-models/
我们使用的解决方案是将消息出列并将其置于“保持表”中,直到所有具有先前序列的消息为止 接收。收到所有以前的消息后,我们全部收到 消息从保持表中出来并按顺序运行它们 适当的处理程序。一旦所有处理程序都被执行了 成功之后,我们从保留表中删除消息并提交 阅读模型的更新。
这适用于我们,因为域发布事件并标记它们 具有适当的序列号。没有这个,解决方案 下面会更加困难 - 如果不是不可能的话。
此解决方案使用关系数据库作为持久性存储 机制,但我们没有使用任何关系方面的 存储引擎。与此同时,所有这一切都有一个警告。 如果消息2,3和4到达但消息1从未到达,则我们不适用 任何一位。只有在出现错误时才会出现这种情况 处理消息1或消息1以某种方式丢失。幸好, 它很容易纠正我们的消息处理程序中的任何错误 重新运行消息。或者,在丢失消息的情况下,重新构建 直接从事件存储中读取模型。
特别提到了几个问题,他说我们总是可以向活动商店询问缺失事件。
答案 0 :(得分:1)
如果您有序列号,那么您可以检测当前事件发生故障的情况,例如: currentEventNumber!= lastReceivedEventNumber + 1
一旦您检测到这一点,您就会抛出异常。如果您的订阅者具有“重试”机制。它将尝试在一秒左右再次处理此事件。很有可能在此期间处理早期事件并且序列将是正确的。如果无序事件很少发生,这是一个解决方案。
如果您经常遇到这种情况,则需要实现全局锁定机制,这将允许按顺序处理某些事件。 例如,我们在MSSQL中使用sp_getapplock来实现全局"关键部分"在某些情况下的行为。当分布式应用程序的多个部分需要的不仅仅是简单的锁定时,Apache ZooKeeper提供了一个框架来处理更复杂的场景。
答案 1 :(得分:1)
基于时间戳的解决方案:
收到的消息是:
{
id: 1,
timestamp: T2,
name: Samuel
}
{
id: 1,
timestamp: T1,
name: Sam,
age: 26
}
{
id: 1,
timestamp: T3,
name: Marlon Samuels,
contact: 123
}
我们希望看到的与数据库中的ORDER无关是:
{
id: 1,
timestamp: T3,
name: Marlon Samuels,
age: 26,
contact: 123
}
对于每条传入的消息,请执行以下操作:
现在让我们看一下消息:
以下deepMerge(src,target)应该能够为我们提供结果:
public static JsonObject deepMerge(JsonObject source, JsonObject target) {
for (String key: source.keySet()) {
JsonElement srcValue = source.get(key);
if (!target.has(key)) { // add only when target doesn't have it already
target.add(key, srcValue);
} else {
// handle recursively according to the requirement
}
}
return target;
}
在评论中让我知道您是否需要DeepMerge()的完整版本
答案 2 :(得分:0)
您在这里描述的是事件采购(ES)。将命令模型发出的事件存储到持久存储。 按事件类型,命令模型ID(聚合根id),命令模型类型(聚合根类型)重播存储的事件。这是ES的优势。您甚至可以稍后重播这些事件以生成新类型的查询模型。 使用ES方法也可以用于具有UnitOfWork范围的应用程序事务。在提交时,发出的事件被持久化并分发给事件侦听器(QM维护服务)。提交阶段的验证应包含按db。中的序列号检查并发访问。
答案 3 :(得分:0)
另一种选择是从(S1)提供您的阅读事件的服务,使其只能为您的服务生成有序事件(S2)。
例如,如果您有许多不同会话的事件加载,请在前端订购服务(O1)负责订购。它确保每个会话只有一个事件传递给(S1),并且只有当(S1)和(S2)都成功处理时(O1)才允许该会话的新事件传递给(S1)。为了提高性能,也要排队等待。