我正在考虑当消息代理突然中断时如何处理发送事件。请看一下这段代码
using (var uow = uowProvider.Create())
{
...
...
var policy = offer.Buy(customer);
uow.Policies.Add(policy);
// DB changes are saved here! but what would happen if...
await uow.CommitChanges();
// ...eventPublisher throw an exception?
await eventPublisher.PublishMessage(PolicyCreated(policy));
return true;
}
恕我直言,如果eventPublisher引发异常,则不会发布PolicyCreated事件。我不知道该如何处理。该事件必须在系统中发布。我想只有好的解决方案才能创建某种重试机制,但是我不确定...
答案 0 :(得分:0)
您将要回顾Udi Dahan对Reliable Messaging without Distributed Transactions的讨论。
但非常概括地说,PolicyCreated
事件成为工作单元的一部分;要么是因为它保存在Policy表示形式本身中,要么是因为它保存在与策略存储库参与同一事务的EventRepository中。
一旦您捕获了数据库中的信息,就可以相对简单地重试发布-从数据库中读取事件,进行发布,还可以选择将数据库中的事件标记为成功发布,以便可以将其清除。 / p>
答案 1 :(得分:0)
对于可靠的系统,您需要在本地保存事件。如果您的经纪人破产,则必须重试并发布事件。
有很多方法可以实现这一目标,但是最常见的是发件箱模式。就像您的邮箱一样,您的事件/消息仍保留在本地,并且您一直在重试直到发送出去,然后将消息标记为发布在本地数据库中。
您可以在这里Publish Events
了解更多信息答案 2 :(得分:0)
我想详细说明@Imran Arshad和@VoiceOfUnreason提供的答案,这些答案当然是正确的。
在发布消息方面,基本上有3种模式:
以下仅是您的示例。
对于exactly once
传递,数据库和队列都需要具有加入分布式事务的能力。某些队列并不能立即提供此功能(例如RabbitMQ),即使有可能自己滚动,也不是最好的选择。分布式事务通常很慢。
对于at most once
传递,我们必须接受我们可能会错过消息的情况,我猜测在大多数用例中这很麻烦。您可以通过跟踪进度并获取丢失的消息并根据需要重新发送,来解决此问题。
对于at least once
传递,我们需要确保消息是幂等的。当我们收到重复的消息时(通常是非常严重的情况),应将其忽略,否则其结果应与处理的初始消息相同。
现在,有两种方法可以解决您的问题。您可以启动数据库事务并进行数据库更改。在确认之前,您要执行消息发送。如果失败,那么您的交易将被回滚。这对于发送一条消息很有效,但是在您的情况下,某些订阅者可能已经收到一条消息。这使事情变得复杂,因为您的所有订户都需要接收该消息,否则任何人都无法接收该消息。
您可以让订户检查状态是否确实为真,以及是否应继续处理。这给订户带来负担并引入了一些耦合。如果状态不允许处理,则可以推迟操作,也可以忽略它。
另一种选择是,您不向自己发送事件,而是向自己发送指示该步骤已完成的命令。命令处理程序将执行发布并重试,直到所有订阅者队列都收到消息为止。这将要求相关的订户忽略他们已经处理过的消息(幂等)。
outbox
是一种存储转发方法,最终会将消息发送给所有订阅者。您可能将outbox
包含在数据库事务中。在我的Shuttle.Esb服务总线中,使用它的人之一遇到了我未曾计划过的怪异副作用。他使用sql-based queue作为发件箱,并且队列连接到同一数据库。因此,它已包含在数据库事务处理中,如果未提交,则会回滚所有其他更改。为推广我自己的产品而道歉,但我确定其他服务总线产品可能具有相同的功能。
因此,有很多事情需要考虑,还有各种技术可以减轻队列中断的风险。但是,我将队列交互移到数据库提交之前。