Transactions MQ-如何在非事务性、轻量级环境中保证消息传递?

Transactions MQ-如何在非事务性、轻量级环境中保证消息传递?,transactions,zeromq,microservices,mq,2phase-commit,Transactions,Zeromq,Microservices,Mq,2phase Commit,如何在非事务性、轻量级环境中保证消息传递 例如: 正常情况:写入数据库,提交,将消息发送到ZeroMQ | Redis | OtherMQ,消费者提取消息以继续处理 0,05%的情况:写入数据库,提交,应用程序死亡,未发送消息,没有消费者拉取消息,处理不完整 在这种情况下,如何不丢失信息(避免不发送信息) 编辑:消息必须准确传递一次。在这种情况下,您有两个共享资源(数据库和队列),并且希望它们一起处理。如果消息已发送到队列,则希望数据库提交。如果数据库未成功发送,则希望数据库不提交,反之亦然

如何在非事务性、轻量级环境中保证消息传递

例如:

  • 正常情况:写入数据库,提交,将消息发送到ZeroMQ | Redis | OtherMQ,消费者提取消息以继续处理
  • 0,05%的情况:写入数据库,提交,应用程序死亡,未发送消息,没有消费者拉取消息,处理不完整
在这种情况下,如何不丢失信息(避免不发送信息)


编辑:消息必须准确传递一次。

在这种情况下,您有两个共享资源(数据库和队列),并且希望它们一起处理。如果消息已发送到队列,则希望数据库提交。如果数据库未成功发送,则希望数据库不提交,反之亦然。这只是像2PC一样的全局事务机制。然而,实现全局事务机制并不是那么容易,而且成本也非常高

我建议您至少在生产者端实现一种策略,在消费者端实现幂等性,以提供一致性

您应该在生产者端的数据库上创建一个消息表,并在发送到队列之前将消息持久化到此表。然后使用一个调度线程(这里可能有多个线程来增加吞吐量,但如果您的消息需要按照它们产生的顺序使用,请小心)或任何其他东西,您可以将它们发送到队列并将它们标记为已发送,以确保已发送的消息不会再次发送。即使您这样做,在某些情况下也可能会多次发送消息(例如,您将消息发送到队列,并且您的应用程序在将消息标记为已发送之前崩溃)。但这不是问题,因为我们已经希望在生产者端实现至少一次策略,这意味着我们希望消息至少被发送到队列一次

为了防止使用者使用在生产者端多次生成的相同消息,您应该实现幂等使用者。简单地说,您可以将已消费消息的id保存到使用者端的数据库表中,并且在处理来自队列的消息之前,您可以检查它是否已被消费。如果它已经被消费,你应该忽略它并获得下一条消息


当然,在微服务环境中提供一致性还有其他选择。你可以在这个很棒的博客上找到其他解决方案-。我上面解释的解决方案也存在于这个博客中。您可以在“使用本地事务发布事件”部分找到它。

这里可能是一个简单的方法

假设您拥有该事务:

  • 将数据写入数据库
  • 通过ZMQ发送消息
  • 向数据库写入发送正常的消息
  • 因此,假设您的应用程序在执行步骤2或步骤3时崩溃。如果是这样,您不知道最后一条消息是否收到了客户队列,在没有最后一次确认的情况下重新启动所有消息后,您必须重新发送(步骤3)

    问题出在消费者方面,因为他们可能会收到两次消息。为了解决这个问题,您可以在每条消息中发送一个事务ID,该ID总是在增加。消费者必须注意最后一条消息的交易ID。当传入消息的事务ID不高于上一条消息的事务ID时,可以忽略该消息


    现在的问题是,您是否可以修改消息结构以及可以使用哪个事务ID。

    当生产者发送消息时,将数据库中的消息标记为“已发送”,在启动时获取所有“未发送”消息,而不是?除非您可以将未发送消息的信息存储在某个地方,否则没有其他方法。@cassandrad在消息发送到MQserver之后,但在应用程序标记消息已在db中发送之前,应用程序可能会死亡。其他选项:在同一个db事务中,消息(在db表中)仍在等待使用者。提交、发送消息、使用者提取消息,使用者必须做的第一件事是在db中将消息标记为已接收。但我认为这种方法对于高频消息来说比较慢,“在消息发送到MQserver之后,但在应用程序标记消息以db形式发送之前,应用程序可能会死亡”。没问题,应用程序启动后会再次发送消息,不是吗?您没有指定传递选项应该正好是一次。但是,如果邮件再次发送,同一邮件将被使用两次或更多次。邮件必须正好传递一次。然后您需要在问题中指定,因为这很重要。