消息代理不在时如何处理发布事件?

时间:2019-07-25 19:17:09

标签: domain-driven-design microservices event-driven

我正在考虑当消息代理突然中断时如何处理发送事件。请看一下这段代码

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事件。我不知道该如何处理。该事件必须在系统中发布。我想只有好的解决方案才能创建某种重试机制,但是我不确定...

3 个答案:

答案 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作为发件箱,并且队列连接到同一数据库。因此,它已包含在数据库事务处理中,如果未提交,则会回滚所有其他更改。为推广我自己的产品而道歉,但我确定其他服务总线产品可能具有相同的功能。

因此,有很多事情需要考虑,还有各种技术可以减轻队列中断的风险。但是,我将队列交互移到数据库提交之前。