可靠的JMS与分布式事务


这篇Atomikos公司的技术提示文章仔细研究了“可靠”的消息传递方式以及如何实现它。正如我们将看到的,这完全取决于您如何配置和使用JMS。

关于JMS
JMS(Java消息服务)是一种从Java或J2EE应用程序中访问消息服务器的接口技术。在过去几年中,JMS因其能够提供所谓的可靠消息传递而在EAI(企业应用程序集成)业务中获得了极大的欢迎。这篇技术提示仔细研究了“可靠”的消息传递方式以及如何实现它。正如我们将看到的,这完全取决于您如何配置和使用JMS。但首先是一些词汇。

递交保证
在本技术提示的上下文中,我们将区分消息的不同传递保证:

  • 也许Maybe:在这种情况下,实际上没有保证。消息可能会消失之前消失。
  • 至少一次:系统中的消息永远不会丢失,但可以多次传递给接收者(因此不止一次消费使用)。这意味着接收消费者需要在编程时考虑到这一点。
  • 恰好一次:消息永不丢失,只发送一次。这是真正可靠的消息传递模式。

使用JMS实现可靠的消息传递
消息传递应用程序是否可靠取决于以下参数:
  • 消息是否在JMS服务器中保留。
  • 接收器使用哪种确认模式。

显然,如果消息没有持久存在,那么总是有可能丢失消息(如果服务器在消息发送之前崩溃)。所以在这种情况下,你只能指望Maybe语义。

但是,即使消息持续存在,仍然有可能丢失消息(在这个技术提示中,'丢失'消息比JMS规范中定义的要宽一些:我们说如果消息处理失败就会丢失消息在接收器的DBMS中)。对于持久消息传递,所有都取决于确认模式。对消息的确认会触发删除服务器上的消息。

消息确认模式
JMS中存在以下模式:

  • AUTOMATIC ACKNOWLEDGMENT自动确认:这意味着一旦接收者得到消息,就会确认(删除)消息。
  • EXPLICIT ACKNOWLEDGEMENT显性确认:这意味着接收方必须明确告诉服务器何时不再需要该消息。
  • TRANSACTIONAL ACKNOWLEDGMENT事务确认:如果接收方获取JTA事务范围内的消息,则当且仅当事务提交时才进行确认。

实现完全一次保证

下表总结了这些参数的每种可能组合的消息保证:

为了获得可靠的消息传递,需要启用事务和持久性。否则,准备好处理消息丢失或重复消息......

事情可能出错的一个例子
人们很容易相信使用JMS就足以实现完全可靠的消息传递,但事实并非如此。
让我们考虑以下关于事情可能出错的例子。假设消息进入JMS队列(例如,支付订单),我们想要在数据库中处理付款。

  1. 消息将从队列中删除
  2. 消息由我们的Java程序处理
  3. 结果放在数据库中

现在让我们关注以下问题:
  • 邮件会丢失吗?
  • 会收到两次消息吗?

每个问题的答案取决于JMS的确认模式。同样,可能的值是:自动,显式和事务。

通过自动确认,JMS消息一旦被取消就会从队列中删除。显然,如果我们的程序在第2步崩溃,这会导致消息丢失。在这种情况下,付款订单将永远丢失。因此,我们会有消息丢失。

通过显式确认,我们的程序中通知JMS之前,它不会从队列中删除消息。我们何时可以告诉JMS删除该消息?如果在步骤2之前完成,则2中的崩溃意味着再次丢失消息。如果在步骤2中完成,则崩溃也将丢失消息,因为数据库尚未更改。如果在步骤3之后完成,那么在我们可以删除消息之前可能发生崩溃。在这种情况下,将在重新启动时重新传递消息,并将重新处理并重新发布到数据库。那就是:重复。

使用事务确认,有两种可能:连接器级JMS和JDBC事务或JTA / XA。在事务模式下使用JMS时,仅当事务提交时才会删除消息。JDBC意味着只有在事务提交时才更新数据库。

对于连接器级事务,我们将有两个不同的事务:一个用于JMS,一个用于JDBC。所以问题是:如何命令提交以避免消息丢失和重复?

如果首先提交JMS事务,则在提交JDBC事务之前可能会发生崩溃。后果:消息丢失; 付款永远不会在数据库中。

如果首先提交JDBC事务,则在提交JMS事务之前可能会发生崩溃。后果:重复的消息; 邮件不会从队列中删除,将在以后重新传递。应用程序将重新处理它并将其重新发布到数据库中。

唯一剩下的可能性是:联合提交JMS和JDBC事务,这正是JTA / XA为您所做的。对于可靠,准确的消息,您确实需要JTA / XA。

(banq注:JTA/XA无法避免分布式环境中的CAP定理,二次提交时发生网络抖动也是有可能的)