Jes*_*pez 6 c# events message-queue eventual-consistency rabbitmq
请假设您有类似以下的方法:
public void PlaceOrder(Order order)
{
this.SaveOrderToDataBase(order);
this.bus.Publish(new OrderPlaced(Order));
}
Run Code Online (Sandbox Code Playgroud)
将订单保存到数据库后,会将事件发布到消息排队系统,因此同一台计算机或其他计算机上的其他子系统可以处理该事件.
但是,如果this.bus.Publish(new OrderPlaced(Order))呼叫失败会发生什么?或者在将订单保存到数据库后机器崩溃了?事件未发布,其他子系统无法处理.这是无法接受的.如果发生这种情况,我需要确保最终发布该事件.
我可以使用哪些可接受的策略?哪个最好?
注意:我不想使用分布式事务.
编辑:
Paul Sasik非常接近,我认为我可以达到100%.这就是我的想法:
首先在数据库中创建一个表,如下所示:
CREATE TABLE Events (EventId int PRIMARY KEY)
Run Code Online (Sandbox Code Playgroud)
您可能希望使用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
Run Code Online (Sandbox Code Playgroud)
所有活动必须包括EventId.当事件订阅者收到事件时,他们首先检查数据库中是否存在EventId.
通过这种方式,您可以获得100%的可靠性,而不仅仅是99.999%
在此视频和此博客文章中解释了确保事件最终发布到消息队列系统的正确方法
基本上,您需要将要发送到数据库的消息存储在您执行业务逻辑操作的同一事务中,然后将消息异步发送到总线并在另一个事务中从数据库中删除该消息:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
因为 PublishEventAsync 可能会失败,您必须稍后重试,因此您需要一个后台进程来重试失败的发送,如下所示:
foreach (ev in eventsThatNeedsToBeSent) {
await PublishEventAsync(ev);
}
Run Code Online (Sandbox Code Playgroud)