【问题标题】:How to avoid too many IBM MQ channel instances?如何避免过多的 IBM MQ 通道实例?
【发布时间】:2017-01-26 04:38:34
【问题描述】:

我构建了一个小型 Java 程序,它使用 MQ 客户端类与 Websphere MQ 服务器和其上的队列通信。我想要的是定期查询某个队列的大小、错误深度,例如每 10 秒一次。

我所做的,示意性的:

  • qm = new MQQManager()
  • q = qm.accessQueue()
  • depth = q.getCurrentDepth()
  • (用depth做点什么)
  • q.close()
  • qm.close()

这就像一个魅力!

出于懒惰,我编写的代码每次都经过上述序列的所有 6 个步骤。所以我最终每 10 秒重复一次这个循环。大约半小时内,监控服务器的人告诉我,我在上面打开了 153 个实例。显然,仅在队列中执行close() 和 QM 并不足以在我自己之后进行清理。

我将进行明显的修复,并在程序的生命周期内保留 QM 和队列。但是有人可以告诉我为什么我以前的方法会泄漏资源,如果我选择走这条路,我能做些什么?


更新 (2017-01-25)

时机接受

Roger 提供了一些关于性能和未提交消息主题的漂亮代码和一些有用的解释。这很酷,但尤金的回答完全(并且最低限度)足以解决我的问题,而且他的速度更快。在我根据 Eugene 的建议将 close() 更改为 disconnect() 之后,我的小应用程序现在正在执行我想要的操作。现在我有点担心谁应该得到复选标记。我向你们俩道歉!

我得到了我想要的东西,我想我已经完成了这个问题,但我应该补充一点,我感谢 Roger 的深思熟虑的回答,它为这个问题的任何未来可能的其他读者提供了有用的信息。希望其他人会听他的,而不是跟随我的领导。

性能

说实话,我不在乎性能。服务器比它当前正在做的需要的要胖得多,我的监控客户端(这个问题的主题)在通常是空的开发机器上运行。我希望它会在一两天内达到它的目的,然后我会把它扔掉。同时,如果每 10 秒连接一次导致性能问题,则应将负载平衡到 Raspberry Pi 或其他设备上。

未提交的消息

我认为这也不是问题。发送应用程序在几毫秒内将大约 1-5 条消息的小批量排队并立即提交。接收应用程序(我写的一个客户端,它很可能有问题)不断地监听队列,分别接收和确认(提交)每条消息。所以我有理由确定永远不会有超过 1 条,可能是 5 条未确认的消息。这个数字与...的参数相比微不足道。

我的实际问题

这涉及到几条到几百条消息,每天早上大约在同一时间处理 5 到 20 分钟之间的延迟。我怀疑我的接收客户端被 MQS 以外的“挂起”网络 IO 操作所阻止。在探索这一点时,我想采取的一个步骤是证明在 MQS 上确实存在等待消息的队列,并且它没有被拾取。我想看看这个队列什么时候开始增加,它在那里坐了多长时间,一旦我的客户再次醒来,它会以多快的速度消退。

此连接每天可看到 2,000 到 10,000 条消息,峰值对应于下午和晚上的批处理,但延迟仅发生在早上,通常实际通过的数量很少。我想查看队列大小的日志,以便更好地了解正在发生的事情。下一步,我可能需要在接收客户端中进行更多检测。

环境

不管怎样,服务器运行的是 WSMQ 6,所以我的客户端使用阻塞和等待队列获取,而不是我更喜欢使用的漂亮的异步通知过程。我的接收客户端是在虚拟机中运行在 RHEL 下的独立 C 应用程序。

【问题讨论】:

  • Roger 的回复包括DISCONNECT描述了为什么CONNECT 每 10 秒如此耗费资源,不应该这样设计, 提供工作示例代码进行演示。惊讶地发现这不是公认的答案。
  • @T.Rob:简单的解释:罗杰的答案很棒,但在我接受的时候,尤金的答案是唯一的。哦等等 - 我知道我现在可以改变它了。完成,谢谢指点!

标签: java ibm-mq instances resource-leak


【解决方案1】:

哇!不是很有效的代码。连接是迄今为止最昂贵的 MQ API 调用。此外,获取队列深度是没有意义的。它为您提供队列中所有未提交和已提交的消息。即队列深度可能会说队列中有 5 条消息,但您的程序可能只检索 3 条!您会说 MQ 已损坏,但实际上有 2 条消息未提交,因此消费者无法使用。

如果您真的想每 10 秒获取并记录一次信息,请保持连接。

这里有一个更好的方法:

boolean working = true;
int openOptions  = CMQC.MQOO_INQUIRE + CMQC.MQOO_FAIL_IF_QUIESCING;
int depth = 0;
MQQueueManager qm = null;
MQQueue q = null;

try
{
   qm = new MQQueueManager("MQA1");

   while (working)
   {
      try
      {
         q = qm.accessQueue("TEST.Q1", openOptions);
         depth = q.getCurrentDepth();
//         (do something with depth)
      }
      catch (MQException e)
      {
         System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
         System.out.println(e.getLocalizedMessage() );
         working = false;
      }
      finally
      {
         if (q != null)
         {
            q.close();
            q = null;
         }
      }

      try
      {
         if (working)
            Thread.sleep(10*1000); // time in milliseconds
      }
      catch (InterruptedException ie) {}
   }
}
catch (MQException e)
{
   System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
   System.out.println(e.getLocalizedMessage() );
}
finally
{
   try
   {
      if (q != null)
         q.close();
   }
   catch (MQException e)
   {
      System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
      System.out.println(e.getLocalizedMessage() );
   }

   try
   {
      if (qm != null)
         qm.disconnect();
   }
   catch (MQException e)
   {
      System.out.println("CC = " + e.completionCode + " : RC=" + e.reasonCode);
      System.out.println(e.getLocalizedMessage() );
   }
}

【讨论】:

  • 天哪,你已经完成了我的大部分工作!谢谢。如果您想添加更多讨论,我将在我的问题文本中回复您的评论以进行澄清。
【解决方案2】:

我认为你真的应该在 finally 块中使用 disconnect 而不是 close。

【讨论】:

  • 比较 QM.close() 和 .disconnect() 的文档,很明显 disconnect() 是我应该使用的。谢谢!!
猜你喜欢
  • 1970-01-01
  • 2012-06-23
  • 2016-01-05
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多