分布式数据库事务中的RabbitMQ和交付保证

时间:2017-02-21 16:26:00

标签: rabbitmq amqp spring-amqp spring-rabbitmq

我试图了解在分布式数据库事务的上下文中处理RabbitMQ交付的正确模式是什么。

为了简单起见,我将用伪代码说明我的想法,但我实际上是使用Spring AMQP来实现这些想法。

这样的东西
void foo(message) {
   processMessageInDatabaseTransaction(message);
   sendMessageToRabbitMQ(message);
}

当我们到达sendMessageToRabbitMQ()时,processMessageInDatabaseTransaction()已成功将更改提交到数据库,或者在到达发送代码的消息之前已抛出异常。

我知道对于sendMessageToRabbitMQ(),我可以使用Rabbit transactionspublisher confirms来保证Rabbit收到我的信息。

我的兴趣是了解当事情向南发生时应该发生什么,即当数据库事务成功时,但确认在一定时间后没有到达(发布者确认)或者Rabbit事务未能提交(使用Rabbit事务)

一旦发生这种情况,保证传递信息的正确模式是什么?

当然,在开发了幂等消费者之后,我认为我可以重新发送消息,直到Rabbit确认成功:

void foo(message) {
   processMessageInDatabaseTransaction(message);
   retryUntilSuccessFull {
      sendMessagesToRabbitMQ(message);
   }
}

但是这种模式有一些我不喜欢的缺点,首先,如果故障延长,我的线程将开始阻塞,我的系统最终会变得无法响应。其次,如果我的系统崩溃或关闭会发生什么?我永远不会传递这些消息,因为它们会丢失。

所以,我想,好吧,我必须首先将我的消息写入数据库,处于挂起状态,然后从那里发布我的待处理消息:

void foo(message) {
   //transaction commits leaving message in pending status
   processMessageInDatabaseTransaction(message);
}

@Poller(every="10 seconds")
void bar() {
   for(message in readPendingMessagesFromDbStore()) {
      sendPendingMessageToRabbitMQ(message);
      if(confirmed) {
          acknowledgeMessageInDatabase(message); 
      }
   }
}

如果我未能确认数据库中的消息,可能会多次发送消息。

但现在我已经介绍了其他问题:

  • 需要从数据库进行I / O以发布消息,即99%的时间可以立即成功发布,而无需检查数据库。
  • 从现在起我已经为发布消息增加了延迟,使得轮询器更接近实时传送的难度。
  • 也许还有其他一些复杂问题,例如保证按顺序交付事件,轮询执行人员相互踩踏,多个投票人等等。

然后我想好了,我可以让它变得有点复杂,我可以从数据库发布,直到我赶上事件的实时流然后发布实时,即保持大小为b的缓冲区(循环我根据页面读取时检查该消息是否在缓冲区中。如果是,那么切换到实时订阅。

到目前为止,我意识到如何做到这一点并不是很明显,所以我得出结论,我需要了解解决这个问题的正确模式。

那么,是否有人建议正确的方法是什么?

2 个答案:

答案 0 :(得分:2)

当Rabbit无法收到消息时(无论出于何种原因,但根据我的经验,仅因为服务已关闭或不可用),您应该能够捕获错误。此时,您可以记录该 - 以及任何后续 - 尝试失败,以便在Rabbit再次可用时重试。最快的方法是将消息详细信息记录到文件中,然后迭代以重新发送(如果适用)

只要您拥有该文件,就不会丢失消息。

一旦消息在Rabbit内部,并且您对架构的其余部分有信心,那么应该可以安全地假设消息将最终到达它们应该存在的位置,并且没有进一步的持久性工作需要在你的最后做。

答案 1 :(得分:2)

虽然RabbitMQ无法参与真正的全局(XA)事务,但您可以使用Spring事务管理将数据库事务与Rabbit事务同步,这样,如果更新失败,则两个事务都将回滚。有一个(非常)小的定时孔,一个人可能会承诺而不是另一个,所以你需要处理这种可能性。

有关详细信息,请参阅Dave Syer's Javaworld Article