JMS 事务如何与并发消费者一起工作?

How do JMS transactions work with concurrent consumers?

嘿,我有一个需要顺序处理的消息队列。现在,此处理涉及调用 Web 服务(有时可能会关闭),因此我必须使消息检索具有事务性。据我所知,当中途出现异常时,整个事务回滚,消息不会丢失吧?

但是还需要消息使用者的高可用性,所以我有两个侦听器实例在我的队列中侦听。现在,事务是否会确保在第一个消息完全处理完第一条消息之前,监听器的另一个实例不会检索到第二条消息?或者我是否必须做更多的事情来确保在前面的消息完全完成处理之前,不会将消息从队列中发送出去。

如果需要任何额外配置,是在 MQ 中还是在侦听器中?

我正在使用 websphere mq 作为消息代理,并使用 spring 集成来检索消息。谢谢您的帮助。

编辑:

关于令牌,首先要考虑的是队列管理器本身的高可用性。持有此令牌的队列必须是某个队列管理器的一部分。现在,如果我们进行故障转移,将无法再访问该控制队列。这有点意味着我们需要准备好另一个控制队列以防发生故障转移。

我们不能让侦听器在正常操作期间侦听该 DR 控制队列。 (假设我们有一种机制可以真正确保 "data" 队列被完美复制)。侦听器实例应该知道故障转移已启动,以便它可以在正常操作期间停止侦听控制队列并切换到辅助队列。我不能单独使用侦听器实例来执行此操作。将消息放入队列的实际生产者必须通知监听器实例停止监听正常的操作控制队列并切换到辅助队列。如果存在任何中间连接问题(并且正常的 ops 队列管理器并没有真正关闭),这将有点棘手,但这太过分了。

随着控制队列的高可用性得到处理,我们在低负载场景中遇到了与不可共享的问题相同的问题。现在我们偶尔会出现负载峰值,但也有低迷时期。 (在夜间和东西)。这个代币系统不是真正的反应式,对吧?这更多是周期性的事情。假设我们在几个小时内没有收到任何消息。听众仍将不断检查队列,因为令牌消息不断触发一个又一个实例。这或多或少使它成为真正的轮询器。我还不如有多个侦听器实例,每个侦听器实例都在每小时的不同时间进行轮询,对吧?它本身并不是真正的事件驱动。

第三个确实是插入令牌消息的问题。在首次安装或故障回复期间,我们将有手动插入此令牌的额外手动步骤(因为令牌有时会在故障转移中丢失)。我们不能真正让其中一个侦听器实例执行此操作,因为如果侦听器实例未找到消息,则表示其他某个侦听器实例具有令牌。所以这个逻辑必须是分开的。如果我们真的把一些有意义的信息放入这个令牌消息中,它必须是一个必须被触发的实用程序,而不是通过 UI 插入。

我想第一个和第三个并不是真正的问题,只是额外的开销,如果我们采用轮询器实现则不需要这些开销。第二个是最困扰我的。

关于您的 HA 问题...

如果你有两个队列接收器从一个队列中读取数据,我不知道有什么自然的方法可以避免它们之间的并行性。

也许是一些特定于供应商的功能,但这似乎是一件奇怪的事情,我不会抱有任何高期望。

如果您在同步点下处理消息,确实可以回滚事务,以便在发生错误时将处理过的消息放回输入队列。 如果程序异常结束,则发生隐式退出。

http://www-01.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q026800_.htm?lang=en

消息的事务处理不会阻止两个不同的消费者同时阅读消息。 队列上有一个名为 Shareability 的 属性 ,可以将其设置为 Not Shareable 以防止队列被不同的消费者同时打开。 您应该使用此选项并准备您的应用程序以重试打开队列,这样当第一个实例失败时,第二个实例将打开队列。

您需要传递令牌。工作原理如下:

首先,创建第二个队列并将单个消息放入其中。现在用以下逻辑启动每个程序。

  1. 使用无限或长等待间隔和 FAIL_IF_QUIESCING 选项从同步点下的令牌队列中获取令牌消息。
  2. 将令牌消息放回同一 UOW 中的令牌队列。
  3. 在同一 UOW 下从应用程序队列中获取下一条消息。
  4. 正常处理应用程序的消息。
  5. 提交 UOW。

您可以根据需要使用任意数量的应用程序实例。您将在每个应用程序实例的两个队列中的每一个上看到一个输入句柄。由于独占使用队列,因此没有应用程序实例必须处理错误。

由于只有一个令牌消息并且一次只有一个应用程序可以将其保存在同步点下,因此只有一个应用程序可以主动处理应用程序消息。由于 GET 关闭应用程序队列取决于成功 GET 关闭令牌队列,因此所有应用程序消息都按严格顺序处理。

注意:应用程序将使用与令牌队列中的令牌消息一样多的并发线程来处理应用程序消息。如果有人将另一个令牌消息添加到令牌队列,严格的顺序处理就会丢失。因此,read/write 不得将对该队列的访问权限授予应用服务帐户以外的任何人。此外,通常会构造该令牌消息,以便应用程序可以识别它。如果一条杂散的无关消息落在那里,应用程序应该忽略它并发出警告。

您会看到这两个应用程序之间的消息分布相当均匀。如果您使用两个以上的应用程序,您可能会看到非常 un 均匀分布,因为队列句柄是在堆栈中管理的。当一个实例提交它的 UOW 时,下一个实例的句柄位于堆栈的顶部,因此它获得下一条消息。在处理该消息时,刚刚提交的实例会将其 GET 放置在堆栈顶部。如果您有 3 个或更多监听实例,则只有前两个实例会看到任何流量。

这可确保一次处理一个队列外的消息。它而非确保您不会被骗。

如果您在同步点下执行所有操作,则不会丢失任何消息。但是,有一种情况是检索并处理消息,然后 COMMIT 调用失败。在那种情况下,事务被回滚并且相同的消息再次可用。如果您使用的是单阶段提交而不是 XA,则不会回滚该消息的处理。

好消息是令牌消息也将在同步点下,这解决了孤立的客户端通道需要一段时间才能释放事务的问题。新连接将获得比孤立事务保存在同步点下的消息更早的消息。最终,通道代理超时将消息释放回队列,但有效地将其位置更改为落后于任何在隔离时处理的消息。

在这种情况下,令牌消息也被隔离,因此在这种类型的连接丢失消息处理后暂时停止并等待通道代理超时。如果发生这种情况,只需在具有 UOW 的实例上发出 STOP CHANNEL 命令。


根据特定于此答案的其他问题详细信息进行更新

The queue which holds this token has to be part of some queue manager. Now if we have a failover, that control queue will no longer be accessible. Which kinda means that that we need another control queue ready in case of a failover.

令牌队列与应用程序队列一样可用或不可用。只需要一个。如果应用程序需要 HA,则应使用多实例 QMgr 或硬件 HA 集群。这些共享磁盘,因此故障转移中出现的 QMgr 与应用程序连接到的相同,只是在不同的物理位置。

如果应用程序需要 DR,则可以将 QMgr 日志和数据目录下的磁盘复制到 DR 站点。但是,在主数据中心进行处理时,不应监听这些实例。

The listener instances should know that a failover has initiated so that it can stop listening to the control queue during normal ops and switch over to the secondary. I can't do this using the listener instance alone.

为什么不呢? WMQ 拥有可重新连接的客户端已经很长时间了,v7.0.1 中的多实例功能使重新连接变得非常简单。作为管理员,您的工作是确保只有一个应用程序实例和令牌(不是触发器!)队列可用。在中断期间,客户端进入重试状态而不需要任何应用程序代码来驱动它。它会找到启动的实例并进行连接。

The actual producer which puts messages into the queue will have to notify the listener instances to stop listening to the normal ops control queue and switch over to the secondary.

问题是关于并发消费者的序列化。这是关于生产者和消费者必须在一个共同地点会合的设计。恰好与这个问题重叠的不同问题只是因为序列化使它变得有些复杂。如果您需要探索拓扑,请提出不同的问题。

This token system is not really reactive right? It's more of periodic thing. So let's say we don't get any messages for a few hours. The listeners will still be constantly checking the queue coz the token message keeps triggering one instance after another.

这不使用触发。它使用令牌(不是触发器!)消息,就像文件系统或数据库使用锁来促进序列化一样。无论哪个侦听器获得令牌消息,然后都会在应用程序队列上无限等待。其他听众在令牌(不是触发器!)队列上有一个无限等待的获取。基本上,他们闲置着直到消息到达。零重新连接、零轮询、零 CPU 周期。如果您需要知道它们还活着,请让它们偶尔在应用程序队列中超时。这会在将令牌传递给另一个侦听器的令牌队列上回滚他们的 UOW。

Third would really be the question of inserting the token message. During first install or during a failback, we'll have that extra manual step of manually inserting this token (since the token would be lost in failover sometimes).

为什么?您是否经常遇到 MQ 在同步点下丢失持久化消息?如果是这样,你做错了。 ;-) 在具有严格序列化要求的情况下,一个队列只能有一个活动实例。如果出于某种原因,除了通过磁盘复制之外,还有其他预定义的应用程序队列实例,那么就会有一个令牌实例(不是触发器!)队列也预定义在它旁边,并且一个令牌(不是触发器!)消息在每个队列中等待队列。面对严格的消息顺序要求,肯定没有人会做这样的事情,但如果他们这样做,那些队列肯定会在不使用时被禁用。

We can't really make one of the listener instances do it since if a listener instance doesn't find the message it kinda means that some other listener instance has the token.

正确。侦听器 可以 检查队列深度、事务、输入句柄等,但通常明智的做法是避免将应用程序逻辑与控制逻辑混合。

So this logic has to be separate. And if we actually put some meaningful info into this token message, it has to be a utility that has to be triggered rather than an insertion through the UI.

为什么?您的编码人员处理应用消息中的结构化数据,对吗?如果这被认为要困难得多,那么有人做错了。 ;-) 将格式化令牌(不是触发器!)消息的实例写入队列,然后将其卸载到文件。当需要重新初始化队列时,先使用Q或QLoad清空队列,再将文件加载进去。 That 实用程序将是打开队列以独占使用、检查深度、检查句柄等的实用程序,然后再执行其魔法。当我为咨询客户执行此操作时,我通常定义一个服务来在 MQ 启动时初始化队列 并且 还在应用程序 GUI 中为操作和支持人员提供一个功能。只要管理令牌(而不是触发器)队列的应用程序在这些操作期间获取它以进行独占访问,它是如何完成的或控制应用程序的多少实例并不重要。

通常我也使用消息中的结构向听众发送命令。有真正的令牌消息,然后有命令应用程序实例执行操作的消息。例如,拥有非事务性 "ping" 功能真是太好了。如果我在单个 UOW 中在令牌(不是触发器!)队列上放置的 ping 消息多于我侦听的应用程序实例,我保证会联系所有这些消息。这样我就可以检测到僵尸。根据需要多少检测,侦听器可以通过在日志、控制台、事件队列等中提供状态(正常运行时间、已处理的消息等)来对 ping 做出反应。

I guess the first and third aren't really problems, but just extra overhead which wouldn't be needed if we went for a poller implementation.

这很好,因为这些都是非常标准的东西。问题主要在于序列化的要求与HA/DR的要求冲突。您正在寻找的是全局事务原子性,以实现跨多个物理位置的单个逻辑队列。 IBM MQ 从未尝试提供这一点,尽管 WAS 消息传递引擎已经尝试提供。最接近的 MQ 是使用两个具有消息和事务数据的内存到内存复制的 MQ 设备,但这在光速延迟开始显着影响吞吐量之前只有几英里。它不能满足您的 DR 需求。事实上,nothing 除了同步复制之外,如果您想在 DR 数据中心获得零恢复点,就可以做到这一点。