发布带有Rebus的消息和在事务范围中登记的数据库连接时,机器崩溃

问题描述 投票:2回答:2

假设我在事务范围内有Rebus和数据库连接(例如sql server connection)。将在数据库连接上执行一些数据库操作,并且Rebus发布一些消息,并且事务范围将不会升级到MSDTC(我检查了Windows上没有分布式事务,并且此方案也适用于Linux以及MSDTC不支持)。在事务范围上调用Complete(),它指示数据库连接和Rebus提交。现在让我们假设数据库连接首先提交并成功,并且在Rebus可以提交(=发布消息)之前,机器崩溃。会发生什么?我可以想到这些场景:

  1. 已提交数据库操作,但未发布任何消息(状态不正确)。
  2. 数据库操作被回滚(不确定谁不会参与MSDTC,当机器重新启动时我不认为有人会检查崩溃期间事务发生了什么),并且没有发布消息(正确状态) 。
  3. 提交数据库操作,并在机器重新启动(正确状态)后发布消息(由谁?)。

此外,我使用NServiceBus检查了相同的场景,并且当与MSMQ一起使用时,事务范围被升级到MSDTC,并且NServiceBus的创建者声称总是会有正确的事务结果 - 全部已提交或全部回滚,无论机器是否在交易范围的任何一点崩溃。

.net nservicebus transactionscope rebus
2个回答
1
投票

处理消息时,Rebus按此顺序执行其工作:

  1. 您的处理程序已执行(可能涉及数据库事务以提交您自己的工作)
  2. 发送传出消息
  3. 传入的消息将从队列中删除

由于世界充满了失败,您的程序可能会在这些步骤之间(或期间)的任何时候失败。

如果在(1)之前或期间发生故障,则没有问题,因为您自己的工作(至少在这种情况下)是在可以原子回滚的事务中执行的。

如果在(1)之后和完全完成(3)之前出现问题,那么您将体验Rebus'“至少一次” - 交付保证,这意味着 - 如果发生此类故障 - 消息将至少处理一次,这意味着它可以被处理两次,如果你不幸,可能会更多。

没有逃避这个事实,所以如果你关心这种情况,你需要让你的消息处理程序idempotent

幂等性可以通过多种方式实现:有时由于操作本身就是幂等的(例如,简单地插入接收的数据,将某些字段的值设置为来自消息的值等),有时依赖于能够丢弃过时的数据(例如,如果您可以将数据的“最后更改”值与消息中的更新时间戳进行比较)。

但有时,如果您的系统通过处理重新传递的消息而最终处于不良状态,您需要精心编写代码,例如,通过将处理消息的消息ID存储在对ID具有唯一约束的表中。

棘手的部分是:真正的幂等性要求您模拟所有公开可见的行为,也就是在第二次处理消息时。这意味着处理邮件时发送/发布的所有邮件也必须第二次发送和发布。

正如您可能想象的那样,实现真正的幂等性并不总是微不足道的。

(...)NServiceBus的创建者声称,无论机器是否在交易范围的任何一点崩溃,交易范围总会有正确的结果 - 无论是全部已提交还是全部已回滚(...)

对于分布式事务和两阶段提交,这可能不是真的,因为可能存在第三个结果:所有事务在准备阶段确认,然后其中一个在提交阶段失败(由于网络中断,磁盘)完全或其他一些不可恢复的问题) - 然后交易协调员别无选择,只能让交易暂停,需要人工干预才能让世界继续下去。


1
投票

此外,我使用NServiceBus检查了相同的场景,并且当与MSMQ一起使用时,事务范围被升级到MSDTC,并且NServiceBus的创建者声称总是会有正确的事务结果 - 全部已提交或全部回滚,无论机器是否在交易范围的任何一点崩溃。

正如@ mookid8000所提到的,即使使用分布式事务,也没有100%的保证。原因是2 generals problem。但你可以说,在可靠性方面,使用分布式事务可以胜过其他所有事务。不幸的是,它会在SQL Server中为您的数据创建大量开销和Serializable锁定。 Oracle doesn't even support it。大多数DBA讨厌这个并且有理由。我使用MSMQ和SQL Server构建系统运行良好,但它需要一些思考。

另一件事是大多数资源不支持分布式事务。就像在云,RabbitMQ和许多其他技术中的一切。

@ mookid8000提到的一个很好的解决方案是将每个传入消息的标识符存储到数据库中,并验证该消息是否已被处理。但它并不止于此。想象一下,使用标识符1b068720-b558-4edf-9ebd-7142bc8cd3c0发布的事件。然后我们尝试告诉队列它可以删除消息,但是由于错误我们没有这样做。我们什么时候将消息标识符存储在数据库中并提交该事务?它成功与否?如果我们再次处理传入消息,是否可以在数据库中找到标识符?可能,但是我们在发布活动之前或之后提交了标识符?每一步都会失败!

问题是,该事件是否会再次发布?因为如果它会,用什么标识符?可能是一个新的独特的,像00d13f2b-ce5b-4880-9a5b-2cb541015902。这里的问题是,接收端点如何知道这是相同的逻辑消息并且不应该处理它,因为我们已经处理了消息但是使用了另一个标识符?我们需要尝试确保事件实际发布,但是如果我们再次发布它,它具有完全相同的标识符。否则,另一方的幂等性是非常困难的,如果不是不可能的话!

这就是Outbox Pattern的用武之地。

正如您所看到的,构建分布式系统并确保它们是故障安全的并不容易。如果你有更多问题,你可以随时reach out

© www.soinside.com 2019 - 2024. All rights reserved.