我现在正在学习微服务数据复制,而我遇到的一件事就是提出了确保事件原子性的正确架构。我理解它的方式,基本流程是:
但是,如果例如在步骤1和2之间发生断电怎么办?在一个天真的系统中,这意味着更改仍然存在,但详细说明它们的事件将永远不会发布。我考虑过以下想法以创造更好的保证,但我不太清楚每种方法的优点和缺点:
答:在我的微服务实例中使用嵌入式数据库(如SQLite)来跟踪从提交到主数据库到事件发布的完整事务。
B:在我的主数据库中创建一个事件表,使用数据库事务插入事件并同时提交相关更改。然后,该服务将事件推送到总线,然后再次提交主数据库以将事件标记为已发布。
C:如上所述,在我的主数据库中创建一个Events表,使用数据库事务插入Event并同时提交相关更改。然后,通过(通过服务中的REST /消息或通过数据库挂钩手动)通知已附加新事件的专用EventPusher服务。 EventPusher服务将查询Events表并将事件推送到总线,将每个事件标记为已发布确认。如果在没有任何通知的情况下经过一定的时间,EventPusher将进行手动查询。
上述每个选项的优缺点是什么?我还有其他优先选择吗?
答案 0 :(得分:3)
但是,例如,如果在步骤1和2之间发生断电,该怎么办
考虑以下方法:
using(var scope = new TransactionScope())
{
_repository.UpdateUser(data);
_eventStore.Publish(new UserUpdated { ... });
scope.Complete();
}
此伪代码假定您使用的内容类似于Entity Framework和TransactionScope
因此,即使您的事件存储实现为某些外部服务,在事件存储信号成功之前,您的UpdateUser事务也不会被提交。 当您已经从_eventStore获得响应但尚未提交ORM事务范围时,仍然存在小失败的可能性。在这种最糟糕的情况下,您将最终获得已发布的事件但数据库中缺少的数据始终存储状态的最新快照。实质上,快照对此聚合无效。
如果您的域无法容忍此类风险,您不应将状态/快照存储在关系数据库中。事件存储将是您可以信赖的唯一事实来源(这是许多CQRS / ES从业者推荐的方法)。
B:在我的主数据库中创建一个事件表,使用数据库事务插入事件并同时提交相关更改。然后,该服务将事件推送到总线,然后再次提交主数据库以将事件标记为已发布。
这种方法也可行,但是,您必须重新发明轮子,而不是简单地重复使用事件存储的某些bulletproof implementation。
选项A和C太过异国情调/过度设计,无法认真考虑是否可行。
答案 1 :(得分:2)
我一直想知道同样的事情。
显然,有许多方法可以处理更新数据库和发布相应事件的原子性。
(Pattern: Event-driven architecture)
Application events模式听起来与您的想法相似 一个例子可能是:
Order Service在ORDER表中插入一行并插入一行 将Created事件命令到EVENT表[在单个本地db事务的范围内]。
事件发布者线程 或处理查询EVENT表中未发布的事件,发布 事件,然后更新EVENT表以将事件标记为 公布
(Event-Driven Data Management for Microservices)
如果事件发布者在任何时候崩溃或以其他方式失败,则它未处理的事件仍标记为未发布。
因此,当Event Publisher重新上线时,它会立即发布这些事件。
如果事件发布者发布了一个事件,然后在将事件标记为已发布之前崩溃,则该事件可能会多次发布。
因此,订阅者重复删除收到的消息非常重要。
此外,the answer到stackoverflow问题 - 这可能听起来与你的完全不同,但实质上是问同样的事情 - 链接到几个相关的博客帖子。