我试图了解在分布式数据库事务的上下文中处理RabbitMQ交付的正确模式是什么。
为了简单起见,我将用伪代码说明我的想法,但我实际上是使用Spring AMQP来实现这些想法。
像
这样的东西void foo(message) {
processMessageInDatabaseTransaction(message);
sendMessageToRabbitMQ(message);
}
当我们到达sendMessageToRabbitMQ()
时,processMessageInDatabaseTransaction()
已成功将更改提交到数据库,或者在到达发送代码的消息之前已抛出异常。
我知道对于sendMessageToRabbitMQ()
,我可以使用Rabbit transactions或publisher 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);
}
}
}
如果我未能确认数据库中的消息,可能会多次发送消息。
但现在我已经介绍了其他问题:
然后我想好了,我可以让它变得有点复杂,我可以从数据库发布,直到我赶上事件的实时流然后发布实时,即保持大小为b的缓冲区(循环我根据页面读取时检查该消息是否在缓冲区中。如果是,那么切换到实时订阅。
到目前为止,我意识到如何做到这一点并不是很明显,所以我得出结论,我需要了解解决这个问题的正确模式。
那么,是否有人建议正确的方法是什么?
答案 0 :(得分:2)
当Rabbit无法收到消息时(无论出于何种原因,但根据我的经验,仅因为服务已关闭或不可用),您应该能够捕获错误。此时,您可以记录该 - 以及任何后续 - 尝试失败,以便在Rabbit再次可用时重试。最快的方法是将消息详细信息记录到文件中,然后迭代以重新发送(如果适用)。
只要您拥有该文件,就不会丢失消息。
一旦消息在Rabbit内部,并且您对架构的其余部分有信心,那么应该可以安全地假设消息将最终到达它们应该存在的位置,并且没有进一步的持久性工作需要在你的最后做。
答案 1 :(得分:2)
虽然RabbitMQ无法参与真正的全局(XA)事务,但您可以使用Spring事务管理将数据库事务与Rabbit事务同步,这样,如果更新失败,则两个事务都将回滚。有一个(非常)小的定时孔,一个人可能会承诺而不是另一个,所以你需要处理这种可能性。
有关详细信息,请参阅Dave Syer's Javaworld Article。