Microservices 事件存储可能成为单点故障?

Microservices 事件存储可能成为单点故障?,microservices,cqrs,event-sourcing,eventstoredb,Microservices,Cqrs,Event Sourcing,Eventstoredb,几天以来,我一直在试图弄清楚如何通知其他微服务,在微服务中创建了一个新实体,并将该实体存储在MongoDB中 我想: 微服务之间的耦合度较低 避免微服务之间的分布式事务,如两阶段提交(2PC) 起初,像RabbitMQ这样的消息代理似乎是一个很好的工具,但随后我看到了MongoDB中的新文档和代理中的消息不是原子的问题 通过eventuate.io: 解决此问题的一种方法是添加一个标记,说明文档是否已在代理中发布,并有一个预定的后台进程,该进程在MongoDB中搜索未发布的文档,并使用,当

几天以来,我一直在试图弄清楚如何通知其他微服务,在微服务中创建了一个新实体,并将该实体存储在MongoDB中

我想:

  • 微服务之间的耦合度较低

  • 避免微服务之间的分布式事务,如两阶段提交(2PC)

起初,像RabbitMQ这样的消息代理似乎是一个很好的工具,但随后我看到了MongoDB中的新文档和代理中的消息不是原子的问题

通过eventuate.io:

解决此问题的一种方法是添加一个标记,说明文档是否已在代理中发布,并有一个预定的后台进程,该进程在MongoDB中搜索未发布的文档,并使用,当确认到达时,文档将被标记为已发布(至少使用一次和幂等语义)。该解决方案在和答案中提出

在阅读Chris Richardson的一篇文章时,我最后做了一个很棒的演示,其中一张幻灯片问:

如何在不使用2PC的情况下以原子方式更新数据库和发布事件和发布事件?(双写问题)

答案很简单(在下一张幻灯片上)

更新数据库和发布事件

这是一种基于的不同方法

域存储库负责发布事件,如下所示: 通常会与存储一起位于单个事务中 事件存储中的事件

我认为将存储和发布事件的职责委托给事件存储是一件好事,因为它避免了2PC或后台进程的需要

然而,在某种程度上,这是真的:

如果您依赖事件存储来发布事件,您将有一个 与存储机构紧密耦合

但是,如果我们采用消息代理来与微服务进行通信,我们也可以这样说

更让我担心的是,事件存储似乎成了单一的失败点

如果我们从这个角度看

我们可以看到,如果事件存储关闭,我们就无法创建帐户或转账,从而失去了微服务的优势之一。(尽管系统将继续响应查询)


因此,确认Eventate示例中使用的事件存储是单点故障是正确的?

您所面临的是事件存储的一个实例。基本上,您希望在网络上有两个实体在某些方面达成一致,但是。莱斯利·兰波特证明了这是不可能的

因此,无论您向网络中添加多少新实体(消息队列是一个),您都永远无法100%确定会达成一致。事实上,情况正好相反:添加到分布式系统中的实体越多,最终达成协议的把握就越小


<> P>一个实际的答案是,如果你考虑增加更多的复杂性和单点故障,那么2PC就不是那么糟糕了。如果您绝对不希望出现单点故障,并且希望假设网络是可靠的(换句话说,网络本身不可能是单点故障),您可以尝试P2P算法,例如,但对于两个对等点,我打赌它会简化为简单的2PC。

我们使用NServiceBus中的发件箱方法处理此问题:


此方法要求整个操作的初始触发器作为队列上的消息传入,但效果很好。

您还可以为事件存储中的每个条目创建一个标志,告知此事件是否已发布。另一个进程可以轮询事件存储区中那些未发布的事件,并将它们放入消息队列或主题中。这种方法的缺点是,必须将此队列或主题的使用者设计为消除传入消息的重复,因为这种模式只保证至少一次传递。另一个缺点可能是轮询频率导致的延迟。但由于我们已经进入了最终一致的领域,这可能不是什么大问题

如果我们有两个事件存储,并且每当创建一个域事件时,它都会在这两个事件存储上排队。查询端的事件处理程序处理从两个事件存储中弹出的事件

当然,每个事件都应该是幂等的。
但这难道不能解决事件存储作为单一入口点的问题吗?

这不是mongodb解决方案,但您是否考虑过利用Redis 5中引入的Streams功能来实现可靠的事件存储。看看这个介绍

我发现它具有一系列丰富的功能,如消息跟踪、消息确认以及轻松提取未确认消息的能力。这肯定有助于实现至少一次消息传递保证。它还支持使用“消费者组”概念对消息进行负载平衡,这有助于扩展处理部分


关于您担心成为单点故障的问题,根据文档,流和消费者信息可以跨节点复制并保存到磁盘(我相信使用常规Redis机制)。这有助于解决单点故障问题。我目前正在考虑将其用于我的一个微服务项目。

谢谢Udi!。两个问题:
Bus.Send(new PreparePurchasedProducts())的含义是什么
总线发布(new PurchaseOrderReceived())?消息未在分派步骤中发布?那么这个括号是什么意思呢?我知道如果调度步骤失败,Db Tx将不会回滚