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

3
嘿,我有一些需要按顺序处理的消息队列。现在这个处理过程涉及到调用Web服务(有时可能会出现故障),所以我必须使消息检索具有事务性。据我所知,如果在中途出现任何异常,整个事务都会回滚,而消息不会丢失,对吗?
但是,消息消费者也需要高可用性,所以我有两个监听器实例同时监听我的队列。现在,事务是否会确保第二个监听器实例不会检索到另一个消息,直到第一个监听器完全完成了第一个消息的处理?还是说我需要做更多的工作,以确保在前一个消息完全处理完之前,不会发送队列中的任何消息。
如果需要任何其他配置,它应该在MQ还是监听器上进行?
我正在使用WebSphere MQ作为消息代理,并使用Spring Integration来检索消息。感谢您的帮助。
编辑:
使用令牌的第一个问题是队列管理器本身的高可用性。持有此令牌的队列必须是某个队列管理器的一部分。现在,如果我们发生故障,那个控制队列将不再可访问。这意味着我们需要准备另一个控制队列以备不时之需。
在正常操作期间,我们不能让侦听器侦听DR控制队列。(假设我们有一种机制可以确保“数据”队列完全复制)。侦听器实例应该知道故障转移已经启动,以便在正常操作期间停止监听控制队列并切换到副本。我无法仅使用侦听器实例来完成此操作。实际生产者将消息放入队列中,将通知侦听器实例停止监听正常操作的控制队列,并切换到副本。如果存在任何中间连接问题(而正常操作队列管理器实际上没有故障),这可能会有些棘手,但这是一个极端情况。
处理控制队列的高可用性之后,我们在低负载场景下面临与不可共享相同的问题。现在,我们偶尔会出现负载峰值,但也有低谷期(在夜间等)。这个令牌系统不是真正的反应式吧?它更像是定期的事情。所以,假设几个小时内我们没有收到任何消息。由于令牌消息不断触发一个实例接着另一个实例,因此侦听器仍将不断检查队列。这使其更像是轮询程序。我可能也可以拥有多个侦听器实例,每个实例在不同的小时段轮询吧?它不是真正的事件驱动。

第三个问题是插入令牌消息的问题。在第一次安装或回退期间,我们需要手动插入此令牌(因为令牌有时会在故障转移中丢失)。我们不能让其中一个监听器实例执行此操作,因为如果一个监听器实例找不到消息,这意味着其他某个监听器实例拥有该令牌。因此,这个逻辑必须是独立的。如果我们真的将一些有意义的信息放入这个令牌消息中,那么它必须是一个工具,而不是通过UI插入的方式。

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


这句话的意思是,如果我们进行故障转移,控制队列的那个部分将无法访问。你能修复它吗?但请不要包含"Edit:"、"Update:"或类似的字样,问题应该看起来像是今天写的。 - undefined
3个回答

1
您需要传递令牌。以下是操作方法:
首先,创建第二个队列并放置单个消息。现在使用以下逻辑启动每个程序。
1. 使用无限或长等待间隔和FAIL_IF_QUIESCING选项从令牌队列中同步点获取令牌消息。 2. 将令牌消息在同一UOW中放回到令牌队列中。 3. 在相同的UOW下从应用程序队列中获取下一个消息。 4. 正常处理应用程序的消息。 5. 提交UOW。
您可以使用尽可能多的应用程序实例。每个应用实例上的两个队列将各有一个输入句柄。没有应用程序实例必须处理由于队列的独占使用而导致的错误。
由于只有一个令牌消息和一次只能有一个应用程序在同步点下持有它,因此只有一个应用程序可以主动处理应用程序消息。由于GET来源app队列取决于成功GET来源token队列,因此所有应用程序消息都按严格顺序处理。
注意:应用程序将使用与令牌队列上的令牌消息数量相同的并发线程处理应用程序消息。 如果有人向令牌队列添加另一个令牌消息,则会丢失严格的顺序处理。 因此,不应授予任何人员除应用服务帐户以外的对该队列的读/写访问权限。 此外,通常情况下,令牌消息的结构化使得应用程序可以识别它。 如果一个偶然的无关消息落在那里,应用程序应该忽略它并抛出警告。
您将看到两个应用程序之间的消息分布相当均匀。 如果您使用超过两个应用程序,则可能会看到极不均匀的分布,因为队列句柄是在堆栈中管理的。 当一个实例提交其UOW时,下一个实例的句柄位于堆栈顶部,因此它获得下一个消息。 当它处理该消息时,刚刚提交的实例将其GET放置在堆栈顶部。 如果您有3个或更多的侦听实例,则只有前两个实例会看到任何流量。
这确保了消息逐个从队列中处理。 它不能保证您不会获得重复项。
如果您在同步点下完成所有操作,就不会丢失任何消息。但是,在某些情况下,当检索和处理消息后,COMMIT调用失败。此时,事务将回滚,并且相同的消息再次可用。如果您使用单阶段提交而不是XA,则该消息的处理不会回滚。
好处在于令牌消息也将在同步点下,这解决了孤立的客户端通道需要一段时间才能释放事务的问题。新连接将获取早于由孤立事务保留的消息的消息。最终,通道代理超时将消息释放回队列,但实际上将其位置更改为在被隔离期间处理的任何消息之后。
在这种情况下,令牌消息也被隔离,因此在此类型的连接丢失消息处理暂停并等待通道代理超时。如果发生这种情况,只需在具有UOW的实例上发出STOP CHANNEL命令即可。

根据对此答案的详细问题描述进行更新

持有此令牌的队列必须是某个队列管理器的一部分。如果我们有故障转移,那么该控制队列将不再可访问。这意味着我们需要在故障转移时准备另一个控制队列。

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

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

监听程序实例应知道故障转移已启动,以便在正常操作期间停止侦听控制队列并切换到辅助队列。我不能仅使用监听器实例来完成此操作。

Why not? WMQ早就拥有可重新连接的客户端,而v7.0.1中的多实例功能使重新连接变得非常简单。 作为管理员,您的工作是确保应用程序和令牌(而不是触发器!)队列只有一个实例可用。 在停机期间,客户端会进行重试,而无需驱动任何应用程序代码。 它找到正在运行的实例并进行连接。
将消息放入队列的实际生产者必须通知监听器实例停止监听正常操作控制队列并切换到辅助队列。
问题涉及并发消费者的序列化。这是一个设计问题,其中生产者和消费者必须在一个公共位置会合。不同的问题只是在某种程度上由于序列化而变得复杂。如果需要探索拓扑结构,请提出不同的问题。
这个令牌系统不是真正的反应性吧?它更像是定期的东西。所以假设我们几个小时都没有收到任何消息。听众仍将不断检查队列,因为令牌消息一直在触发一个接一个的实例。

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

第三个问题实际上是关于插入令牌消息的问题。 在第一次安装或退回时,我们需要手动插入此令牌 (因为在故障转移中令牌有时会丢失)。

为什么?你是否经常遇到 MQ 在同步点下丢失持久消息?如果是这样的话,你做错了。在需要严格串行化的情况下,只能有一个队列的活动实例。如果出于某些原因已经预定义了除磁盘复制之外的应用程序队列的其他实例,则还会预定义一个令牌(而不是触发器!)队列,并在每个队列中等待一个令牌(而不是触发器!)消息。当然,如果在要求严格消息顺序的情况下有人这样做,那么这些队列肯定会在不使用时被禁用。
“我们不能让其中一个监听器实例来执行它,因为如果一个监听器实例找不到消息,那意味着其他监听器实例拥有令牌。”
正确。监听器可以检查队列深度、事务、输入句柄等,但通常最好避免将应用程序逻辑与控制逻辑混合。
“因此,这个逻辑必须是单独的。如果我们真正把一些有意义的信息放入这个令牌消息中,那它必须是一个必须通过触发器而不是通过 UI 插入的实用程序。”
为什么?你的编程人员处理应用消息中的结构化数据,对吗?如果这被认为是非常困难的话,那一定是有人做错了。;-) 写一个格式化令牌(不是触发器!)消息实例到队列,然后将其卸载到文件中。当您需要重新初始化队列时,请使用 Q 或 QLoad 首先清除队列,然后将文件加载到其中。那个实用程序将打开队列以独占方式使用,在执行其操作之前检查深度、句柄等。当我为咨询客户执行此操作时,通常会定义一个服务,在 MQ 启动时初始化队列,并在应用程序 GUI 中为运营和支持人员提供功能。只要管理令牌(而不是触发器)队列的应用程序在这些操作期间获得它以进行独占访问,那么它的执行方式或控制应用程序的实例数量就无关紧要了。
作为惯例,我还使用消息中的结构向侦听器发送命令。有真正的令牌消息,还有命令应用程序实例执行任务的消息。例如,具有非事务性“ping”功能非常棒。如果在单个UOW中将更多的ping消息放入令牌(不是触发器!)队列中,而不是正在侦听的应用程序实例,则可以确保联系到它们所有人。通过这种方式,我可以检测僵尸程序。根据所需的仪表化程度,侦听器可以通过记录、控制台、事件队列等方式响应ping并提供状态(正常运行时间、处理的消息数量等)。

我猜第一个和第三个问题并不是真正的问题,只是额外的开销,如果我们选择轮询实现,则不需要它们。

这很好,因为这都是非常标准的东西。问题主要在于序列化要求与HA / DR的要求冲突。您要寻找的是全局事务原子性,以在多个物理位置上实现单个逻辑队列。IBM MQ从未尝试过提供此功能,但WAS Messaging Engine有。MQ最接近的方法是使用两个MQ设备,并将消息和事务数据在内存到内存之间进行复制,但仅适用于轻微延迟,否则会显着影响吞吐量。它无法处理您的DR需求。实际上,除了同步复制外,没有任何东西可以在DR数据中心实现零恢复点。

嘿,所以这个令牌的作用是用来锁定其他并发消费者访问实际“数据”队列的,当另一个实例正在从中获取时,对吧?我在https://dev59.com/SHfZa4cB1Zd3GeqPMQPL#18902573 上看到了你的答案,这正是我们所追求的(稍微加入了一点额外的调整,使用了一个初始化队列而不是这个控制队列),但还有一些其他问题。我会编辑我的问题并提供详细信息。 - alokraop
我正在努力理解如何将触发与这种方法结合起来。我无法想象为什么会使用初始化队列。问题的一部分在于你在评论中提到了生产者/消费者会面的问题。听起来你正在混合拓扑解决方案和序列化解决方案,虽然它们有些重叠,但通常是不同的问题。我的许多客户都在成功地使用令牌进行序列化。据我所知,没有人将其与触发混合使用过。 - T.Rob
哦,所以一个实例获取令牌并执行接收操作,完全没有超时。我第一次没有理解到这一点。太棒了,谢谢。生产者通知监听器是我们正在做的事情,因为我不知道如何让监听器从一个队列切换到另一个队列。我会尝试理解你说的所有其他内容,并在有任何疑问时回来。非常感谢。 :-) - alokraop
通过触发,我们设置了初始化队列在第一条消息到达数据队列时接收一条消息。竞争的消费者之一会取走它并处理数据队列直到它为空。我仍然使用生产者通知我队列管理器何时停止工作,当这种情况发生时,侦听器将从主要的初始化队列上解除,并钩入使用相同触发机制的次要队列。 (我使用控制总线以编程方式实现此操作。我将检查可重新连接的客户端。对于MQ,我就像一个彻底的初学者) - alokraop
祝你好运!一定要点击 [tag:websphere-mq] 标签并查看“入门”资源。我会在这里关注标签,但如果有问题没有得到回答,请随时联系我。联系信息在个人资料中。 - T.Rob
显示剩余2条评论

0

关于你的HA问题...

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

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


0

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

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

消息的事务处理不会阻止两个独立的消费者同时读取消息。队列上有一个名为可共享性的属性,可以将其设置为“不可共享”,从而防止队列被多个消费者同时打开。您应该使用此选项并准备好重试打开队列,以便当第一个实例失败时,第二个实例将打开队列。


是的,我确实尝试了不可共享选项,但一旦其中一个侦听器连接上,其他侦听器就无法连接了,对吧?我不想让第二个侦听器一直出错并在循环中尝试抓取队列。如果第一个侦听器关闭,我没有真正的方法通知第二个侦听器,以便它可以开始侦听。 - alokraop
有关第二个监听器出现错误并重试的争论是什么? - Attila Repasi
这不是很干净,对吧?“异常”的概念就是出了问题。但在这里,情况恰恰相反。除非第二个侦听器不断收到异常,否则系统处于正常操作状态(没有问题)。当异常停止时,这意味着我们处于故障转移状态(出现了问题)。即使从纯性能的角度来看,它也占用了大量内存。我在同一 WAS 上有另一个组件,实际上将消息放入这些队列中。如果其中一个侦听器不断尝试连接并失败,我注意到性能会显著下降。 - alokraop
我不知道是否有定期尝试抓取队列的方法,但即使有,那也意味着它并不比轮询更好。我们需要在SLA方面保持一致。说故障转移时消息处理可能会变慢,这意味着我们不够一致。:( - alokraop
根据给定的架构和限制,我看不到其他解决方案。我认为你需要一些方法在消费者应用程序之间发送心跳或其他方式来保持1个且仅1个实例运行,MQ本身无法真正解决这个问题。 - Attila Repasi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接