【问题标题】:How do JMS transactions work with concurrent consumers?JMS 事务如何与并发消费者一起工作?
【发布时间】:2016-02-11 18:16:22
【问题描述】:

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

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

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

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

编辑:

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

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

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

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

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

【问题讨论】:

    标签: java jms ibm-mq messaging


    【解决方案1】:

    您需要传递令牌。以下是它的工作原理:

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

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

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

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

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

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

    这可确保每次处理队列外的消息。它确实保证您不会被骗。

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

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

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


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

    持有此令牌的队列必须是某个队列管理器的一部分。 现在,如果我们有故障转移,该控制队列将不再是 无障碍。这有点意味着我们需要另一个控制队列 为故障转移做好准备。

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

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

    监听器实例应该知道故障转移已经启动,所以 它可以在正常操作期间停止侦听控制队列,并且 切换到二级。我无法使用侦听器执行此操作 单独实例。

    为什么不呢? 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 数据中心实现零恢复点,没有什么可以做到这一点。

    【讨论】:

    • 嘿,所以当另一个实例正在使用它时,令牌有点用来锁定任何其他并发消费者访问实际的“数据”队列,对吧?我在stackoverflow.com/a/18902573/2885370 中看到了您的答案,这就是我们或多或少的目标(应用了使用启动队列而不是此控制队列的一些额外调整),但还有一些其他问题。我将用详细信息编辑我的问题。
    • 我正在尝试用这种方法混合触发。我无法想象如何或为什么会为此使用启动队列。您的 cmets 中提到了有关生产者/消费者集合点的部分问题。听起来您正在将拓扑解决方案与序列化解决方案混合在一起,这些解决方案有些重叠,但通常是单独的问题。我的许多客户都在使用令牌进行序列化并取得了巨大成功。据我所知,没有人将它与触发混为一谈。
    • 哦,所以一个实例获得了令牌并在没有超时的情况下进行接收。我第一次没明白。很好,谢谢。生产者通知听众是我们正在做的事情,因为我不知道如何让听众从一个队列切换到另一个。我会尝试理解你所说的所有其他内容,并在回来时做出任何澄清。非常感谢。 :-)
    • 通过触发我们正在做的事情是设置它,以便当第一条消息到达数据队列时,启动队列会收到一条消息。竞争的消费者之一接受它并处理数据队列,直到它为空。我仍在使用生产者通知我队列管理器已关闭,当它关闭时,侦听器从主启动队列中解脱并连接到使用相同触发机制的辅助。 (我正在使用控制总线以编程方式实现此目的。我必须检查可重新连接的客户端。我对 MQ 完全是个菜鸟)
    • 祝你好运!请务必单击websphere-mq 标签并查看“入门”资源。我试着留意这里的标签,但如果有问题没有得到解答,请随时联系我。联系信息在个人资料中。
    【解决方案2】:

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

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

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

    【讨论】:

    • 是的,我确实尝试了不可共享的选项,但是一旦其中一个听众上钩,其他听众就不能正确了?我真的不希望第二个侦听器不断出错并尝试将自己锁定在循环中的队列中。如果第一个侦听器出现故障,我无法通知第二个侦听器以便它也可以开始侦听。
    • 反对让第二个侦听器出错并重试的论据是什么?
    • 嗯,不是很干净,对吧? “异常”的概念就是出现了问题。但在这里,情况恰恰相反。直到第二个监听器不断收到异常,系统处于正常操作(没有错)。当异常停止时,这意味着我们处于故障转移中(出了点问题)。即使从纯粹的性能角度来看,它也会占用大量内存。我在实际上将消息放入这些队列的同一个 WAS 上有这个其他组件。如果其中一位听众不断尝试附加并失败,我注意到性能会大幅下降。
    • 我不知道是否有任何方法可以定期尝试锁定队列,但即使有,这也意味着它实际上并不比轮询器好。我们需要与我们的 SLA 保持一致。说故障转移时消息处理可能会变慢意味着我们不一致。 :(
    • 使用给定的架构和限制,我看不到任何其他解决方案。我认为您将需要某种方式在您的消费者应用程序之间发送心跳或其他方式来保持 1 个且只有 1 个实例,仅 MQ 无法真正解决这个问题。
    【解决方案3】:

    关于您的 HA 问题...

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

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

    【讨论】:

      猜你喜欢
      • 2013-05-23
      • 2014-09-06
      • 2013-05-30
      • 1970-01-01
      • 2015-04-02
      • 2018-03-09
      • 2018-03-14
      • 2011-06-04
      • 2020-09-12
      相关资源
      最近更新 更多