【发布时间】:2021-10-02 11:42:04
【问题描述】:
我试图弄清楚如何使用 MassTransit 实施容错消息发布解决方案。我们将关注一个简单的场景,我们只需要提交一个数据库更改,并发布一个指示该更改的事件。因为没有(内置)机制允许原子“提交和发布”,当我们的进程崩溃时,我们将进入不一致的状态(一些消息会只提交到数据库,有些可能只发布到消息队列)。
This documentation page 提供了一个解决方案,因为我们假设消息处理是幂等的,所以我们可以依靠在失败的情况下重试整个操作,并且这些部分提交将得到解决最终。这是一个很好的解决方案,但它只有一个警告:它假设我们正在执行的操作是由消息触发的,如果我们不发送 ack,将重试处理。这不是一个合理的假设,因为消息传递通常仅用于系统内部的内部通信,而不用于与外部世界的通信。处理来自外部客户端的 HTTP 请求时,需要保存和发布时应该怎么做?
一种可能的解决方案是破解文章中介绍的方法,只发布(或发送)一条消息,并自己收听它,然后在消息处理程序中我们执行提交并发布实际的我们希望其他人听到的事件。我遇到的主要问题是它假设我们永远不必在 HTTP 响应中返回任何内容。如果我们需要向 HTTP 客户端指示数据库事务的成功或失败怎么办? (例如:如果我们依赖 UNIQUE 约束来告诉我们是否应该接受请求,并且我们想向客户端指示失败)。我们可以通过在消息队列上使用请求-响应(与我们自己)来解决它,但这很丑陋,并且会大大增加延迟和复杂性,这实际上是一种非常常见的场景。
我在互联网上看到最多的解决这个问题的方法是使用一个持久化到我们需要写入的同一个数据库的发件箱,因此我们可以将这两个操作包装在一个常规的 ACID 数据库事务中.然后后台任务轮询该数据库以查找新事件并将它们发布到消息代理。与其他框架不同,我知道 MassTransit 不支持这种开箱即用的行为。所以我想我的问题可以归结为:在自己急于实现这个相对复杂的机制之前(每个数据库技术一次),我还缺少另一个解决方案吗? MassTransit 社区对此问题的公认解决方案是什么?
【问题讨论】:
-
我没有想到其他解决方案。我们确实有一个额外的消息存储和额外的任务,应该从它可靠地发布。这解决了一个问题,但将可能的不一致推向了管道。请注意,它不仅仅与 MassTransit 相关。
-
@WiktorZychla 你是如何实现后台轮询的?您是否假设只有一个消息发布者实例?因为如果不是这样,这将成为一项非常棘手的任务(不会让消息代理充满重复)。
-
是的,只有一个。需要时,它会水平扩展,只有另一个 storage-publisher 副本,应用程序使用循环法来选择要保存到的存储空间。
标签: c# publish-subscribe messaging masstransit