【问题标题】:Can I mark IBM MQ messages as dirty?我可以将 IBM MQ 消息标记为脏消息吗?
【发布时间】:2025-12-14 14:20:22
【问题描述】:

我确实有以下(多线程)进程:

  1. 浏览 MQ 队列(带锁)并获取下一条可用消息
  2. 用它做一些可能失败也可能不会失败的事情
  3. 一个。如果成功,从队列中删除消息并重新开始,或者 b.如果不成功,请在队列中留言

我的问题源于我的应用程序可能在第 2 步和第 3 步之间意外死机,然后应用程序在重新启动时会产生重复的消息。

有没有办法在队列中将消息标记为“脏”或“正在处理”(在读取消息时或读取后),即使应用程序重新启动,该标记仍然存在?

我尝试使用 MQ 提供的标记,但它们无法在重新启动后继续存在。另一种可能性是将消息移动到“处理”队列,成功时将其删除或失败时将其移回源队列,但这需要第二个队列并且不再是微不足道的代码。

粗略的代码示例:

MQGetMessageOptions gmo = new MQGetMessageOptions(); 
gmo.options = MQConstants.MQGMO_BROWSE_FIRST | MQConstants.MQGMO_LOCK;
MQMessage message = new MQMessage();
message.correlationId = MQC.MQCI_NONE;
message.messageId     = MQC.MQMI_NONE;
queue.get(message, gmo);

boolean success = processMessage(message);

// Application gets killed here after successful message processing.
// Produces duplicate after restart.

if (success) {
   MQGetMessageOptions gmo2 = new MQGetMessageOptions(); 
   gmo2.options = MQConstants.MQGMO_MSG_UNDER_CURSOR;
   queue.get(new MQMessage(), gmo2);
}

基本上,我想实现这个:

  • 从队列中无损获取消息(仅当未标记为“处理中”时)
  • 在队列中将消息标记为“处理中”
  • 处理消息(包括发送到某个目的地)
  • 如果成功从队列中删除,否则删除队列上的“处理”状态

如果应用程序在成功的第三步“处理消息”后立即终止,则该消息将被标记为“处理中”并且不会再次被处理(因为它可能已经被处理了)。

注意:我不希望这个过程对消息处理有任何了解(除了成功之外)。

【问题讨论】:

    标签: ibm-mq


    【解决方案1】:

    您是否尝试过 SYNCPOINT?Commit 或 Backout 类型的操作在这种情况下可能会有所帮助。

    【讨论】:

    • 是的,我有。问题仍然存在:如果应用程序在qmgr.commit() 之前死掉怎么办?然后该消息在队列中再次可用。
    • 但它不会是重复的消息。它与回滚的消息相同。我也尝试过您的应用程序,它按预期工作。当您说重复消息时,您的意思是消息回滚?
    • 如果你使用SYNCPOINT,如果消息被回滚,消息的“backout count”属性告诉你消息被访问了多少次。
    • 关于您的第一条评论:我需要避免processMessage(message) 被多次执行。如果我在此语句之后回滚,我将在下次启动应用程序时得到一个副本。关于第二个:我不知道。这确实可以提供帮助。有没有办法区分应用程序调用backout() 和自动退出的消息(因为应用程序崩溃)?
    • 请通过链接。它是关于避免重复传递浏览的消息。它可能对您有帮助ibm.com/support/knowledgecenter/SSFKSJ_7.0.1/…
    【解决方案2】:

    您的解决方案是一个糟糕的设计。如果您正在更新数据库,那么为什么不使用 2 阶段提交(即 XA 事务)?

    只需让您的 MQAdmin 设置队列管理器以使用您正在使用的特定数据库的资源管理器,然后就很简单了:

    • 启动事务(2 阶段提交)
    • 从队列中获取消息(破坏性获取不浏览)
    • 更新数据库
    • 提交事务

    因此,事务中的所有内容,MQGET 和数据库更新,要么一起提交,要么一起撤销。

    如果您的应用程序崩溃,那么资源管理器将自动退出事务中的所有内容。


    假设您不想使用 2 阶段提交或者您没有更新数据库(更新文件),那么您可以使用单阶段 UOW(工作单元)。

    • 使用 MQGMO_SYNCPOINT 的 MQGMO 选项
    • 从队列中获取消息(破坏性获取不浏览)
    • 更新您正在更新的任何内容
    • 问题 MQCMIT

    关于 MQ 的注意事项:

    • 如果应用程序发出 MQDISC 或正常结束,当前未提交的操作,执行隐含的 MQCMIT 由 IBM MQ 执行,即提交在 SYNCPOINT 下完成的所有操作。

    • 如果应用程序异常结束,当前未提交的操作,执行隐含的 MQBACK通过 IBM MQ,即所有在 SYNCPOINT 下完成的操作都被回滚。

    【讨论】:

    • 由于有许多不同的“输入通道”(MQ 只是获取数据的一种方式)和许多处理数据的方式,所以在使用数据库作为目标时,我宁愿没有不同的解决方案.虽然您的第一个建议可以解决问题,但我可能需要对输入和输出的每种组合进行实现。您的第二个建议与我的问题相同:如果应用程序在您的第三步(“更新您正在更新的内容”)之后死机,则该消息将被退回并在重新启动后重新处理。