c# - 确保事件最终发布到消息队列系统的最佳方式

标签 c# events rabbitmq message-queue eventual-consistency

请想象您有如下方法:

public void PlaceOrder(Order order)
{
     this.SaveOrderToDataBase(order);
     this.bus.Publish(new OrderPlaced(Order));    
}

订单保存到数据库后,一个事件被发布到消息队列系统,所以同一台或另一台机器上的其他子系统可以处理它。

但是,如果 this.bus.Publish(new OrderPlaced(Order)) 调用失败会怎样?或者机器在将订单保存到数据库后就崩溃了?该事件未发布,其他子系统无法处理。这是无法接受的。如果发生这种情况,我需要确保事件最终发布。

我可以使用哪些可接受的策略?哪个最好?

注意:我不想使用分布式事务。

编辑:

Paul Sasik 非常接近,我觉得我可以做到100%。这是我的想法:

首先在数据库中创建一个表 Events,如下所示:

CREATE TABLE Events (EventId int PRIMARY KEY)

您可能想使用 guids 而不是 int,或者您可以使用序列或标识。

然后执行以下伪代码:

open transaction
save order and event via A SINGLE transaction
in case of failure, report error and return
place order in message queue
in case of failure, report error, roll back transaction and return
commit transaction

所有事件都必须包含 EventId。当事件订阅者收到事件时,他们首先检查数据库中是否存在 EventId。

这样你就可以获得 100% 的可靠性,而不仅仅是 99.999%

最佳答案

确保事件最终发布到消息队列系统的正确方法在 video 中有说明。和 this blog post

基本上,您需要在执行业务逻辑操作的同一个事务中将要发送的消息存储到数据库中,然后将消息异步发送到总线并在另一个事务中从数据库中删除消息:

public void PlaceOrder(Order order)
{
     BeginTransaction();
     Try 
     {
         SaveOrderToDataBase(order);
         ev = new OrderPlaced(Order);
         SaveEventToDataBase(ev);
         CommitTransaction();
     }
     Catch 
     {
          RollbackTransaction();
          return;
     }

     PublishEventAsync(ev);    
}

async Task PublishEventAsync(BussinesEvent ev) 
{
    BegintTransaction();
    try 
    {
         await DeleteEventAsync(ev);
         await bus.PublishAsync(ev);
         CommitTransaction();
    }
    catch 
    {
         RollbackTransaction();
    }

}

因为 PublishEventAsync 可能会失败,您必须稍后重试,所以您需要一个后台进程来重试失败的发送,如下所示:

foreach (ev in eventsThatNeedsToBeSent) {
    await PublishEventAsync(ev);
}

关于c# - 确保事件最终发布到消息队列系统的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30780979/

相关文章:

c# - 在 WPF 中查找字形的像素大小

c# - 以编程方式如何在 MultiBinding StringFormat 中提供连续的任意数量的空白字符?

javascript - 将属性附加到 Javascript 中的冒泡事件对象

java - RabbitMQ 对 EC2 性能的挑战

docker - 如何启用 RabbitMQ Docker 容器的集群

c# - while循环到linq

c# - list clear 是否删除之前包含的对象

Spring 集成 HTTP 到 Scatter Gather

jquery - 当在类上使用 jQuery 的单击事件时,如何避免为所有具有该类名的事件调用该事件?

rabbitmq - rabbitmq中预取计数与无ack有什么区别