【问题标题】:Reuse MQMessage object重用 MQMessage 对象
【发布时间】:2017-07-02 02:18:38
【问题描述】:

我有这个永远监听消息的测试代码。如果它得到一个,它会打印出来。我想要做的是避免在每个 get() 之前构造一个 MQMessage 对象。如何重复使用 MQMessage 来多次调用 get()?

using System;
using IBM.WMQ;

namespace WMQ {
    class Program {
        static void Main(string[] args) {
            string QueueManagerName = "A1PATA00";
            string channelName = "ECACHE";
            string connectionName = "A1PATA00.WORLDSPAN.COM(1414)";
            var queueManager = new MQQueueManager(QueueManagerName, channelName, connectionName);
            MQQueue get = queueManager.AccessQueue("SPLASH.ECAC.2", MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQGMO_FAIL_IF_QUIESCING);
            var gmo = new MQGetMessageOptions();
            gmo.Options = MQC.MQGMO_FAIL_IF_QUIESCING | MQC.MQGMO_WAIT;
            gmo.WaitInterval = 10000000;// wait time
            // var queueMessage = new MQMessage();  <--- i want to do this new once!
            while (true) {
                var queueMessage = new MQMessage(); // <-- only works if I do this every time i do a get
                get.Get(queueMessage, gmo);
                var strReturn = queueMessage.ReadString(queueMessage.MessageLength);
                Console.WriteLine(strReturn);
            }
        }
    }
}

【问题讨论】:

  • 多么糟糕的 API。这只是来自 PL/1 的直接端口吗?
  • 它肯定是以 c 为中心的!

标签: c# ibm-mq


【解决方案1】:
  1. 当我看到这样的问题时,我会摇头。你不 了解 .NET VM(框架)中的面向对象概念和 垃圾收集。
  2. 另外,为什么不编写纯 C# 代码?
  3. 最后,在一个安全性非常重要的世界里,您的代码可以 不支持 SSL/TLS 和/或用户 ID 和密码身份验证。

(1) 请注意我在哪里定义 MQMessage 对象(非常重要)。

以下2个代码sn-ps在内存使用或速度上基本没有区别:

(A)

MQMessage msg = null;
while (true)
{
   msg = new MQMessage();
   get.Get(msg, gmo);
   Console.WriteLine(msg.ReadString(msg.MessageLength));
}

(B)

MQMessage msg = new MQMessage();
while (true)
{
   get.Get(msg, gmo);
   Console.WriteLine(msg.ReadString(msg.MessageLength));
   msg.ClearMessage();
   msg.MessageId = MQC.MQMI_NONE;
   msg.CorrelationId = MQC.MQCI_NONE;
}

我更喜欢 (A),因为它更简洁、更易于阅读。

(2) 当你使用 'var' 时,你是在强迫 .NET 框架猜测你在做什么。做纯粹的面向对象编程。 即

MQMessage msg = new MQMessage();

(3)在MQQueueManager中显式设置通道名和连接名不允许设置MQ安全信息。此外,不要使用 MQEnvironment 类,因为它不是线程安全的。最好将所有信息放在一个 Hashtable 中并将 Hashtable 传递给 MQQueueManager 类。下面是一个使用 Hashtable 获取 MQ 连接信息的 MQ .NET 托管模式示例:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using IBM.WMQ;

namespace MQTest02
{
   class MQTest02
   {
      private Hashtable inParms = null;
      private Hashtable qMgrProp = null;
      private System.String qManager;
      private System.String outputQName;

      /*
      * The constructor
      */
      public MQTest02()
         : base()
      {
      }

      /// <summary> Make sure the required parameters are present.</summary>
      /// <returns> true/false
      /// </returns>
      private bool allParamsPresent()
      {
         bool b = inParms.ContainsKey("-h") && inParms.ContainsKey("-p") &&
                  inParms.ContainsKey("-c") && inParms.ContainsKey("-m") &&
                  inParms.ContainsKey("-q");
         if (b)
         {
            try
            {
               System.Int32.Parse((System.String)inParms["-p"]);
            }
            catch (System.FormatException e)
            {
               b = false;
            }
         }

         return b;
      }

      /// <summary> Extract the command-line parameters and initialize the MQ variables.</summary>
      /// <param name="args">
      /// </param>
      /// <throws>  IllegalArgumentException </throws>
      private void init(System.String[] args)
      {
         inParms = Hashtable.Synchronized(new Hashtable());
         if (args.Length > 0 && (args.Length % 2) == 0)
         {
            for (int i = 0; i < args.Length; i += 2)
            {
               inParms[args[i]] = args[i + 1];
            }
         }
         else
         {
            throw new System.ArgumentException();
         }

         if (allParamsPresent())
         {
            qManager = ((System.String)inParms["-m"]);
            outputQName = ((System.String)inParms["-q"]);

            qMgrProp = new Hashtable();
            qMgrProp.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);

            qMgrProp.Add(MQC.HOST_NAME_PROPERTY, ((System.String)inParms["-h"]));
            qMgrProp.Add(MQC.CHANNEL_PROPERTY, ((System.String)inParms["-c"]));

            try
            {
               qMgrProp.Add(MQC.PORT_PROPERTY, System.Int32.Parse((System.String)inParms["-p"]));
            }
            catch (System.FormatException e)
            {
               qMgrProp.Add(MQC.PORT_PROPERTY, 1414);
            }

            if (inParms.ContainsKey("-u"))
               qMgrProp.Add(MQC.USER_ID_PROPERTY, ((System.String)inParms["-u"]));

            if (inParms.ContainsKey("-x"))
               qMgrProp.Add(MQC.PASSWORD_PROPERTY, ((System.String)inParms["-x"]));

            if (inParms.ContainsKey("-s"))
               qMgrProp.Add(MQC.SECURITY_EXIT_PROPERTY, ((System.String)inParms["-s"]));

            System.Console.Out.WriteLine("MQTest02:");
            Console.WriteLine("  QMgrName ='{0}'", qManager);
            Console.WriteLine("  Output QName ='{0}'", outputQName);

            System.Console.Out.WriteLine("QMgr Property values:");
            foreach (DictionaryEntry de in qMgrProp)
            {
               Console.WriteLine("  {0} = '{1}'", de.Key, de.Value);
            }
         }
         else
         {
            throw new System.ArgumentException();
         }
      }

      /// <summary> Connect, open queue, read a message, close queue and disconnect.
      ///
      /// </summary>
      /// <throws>  MQException </throws>
      private void testReceive()
      {
         MQQueueManager qMgr = null;
         MQQueue queue = null;
         int openOptions = MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING;
         MQGetMessageOptions gmo = new MQGetMessageOptions();
         MQMessage receiveMsg = null;

         try
         {
            qMgr = new MQQueueManager(qManager, qMgrProp);
            System.Console.Out.WriteLine("MQTest02 successfully connected to " + qManager);

            queue = qMgr.AccessQueue(outputQName, openOptions, null, null, null); // no alternate user id
            System.Console.Out.WriteLine("MQTest02 successfully opened " + outputQName);

            receiveMsg = new MQMessage();

            queue.Get(receiveMsg, gmo);
            System.Console.Out.WriteLine("Message Data>>>" + receiveMsg.ReadString(receiveMsg.MessageLength));
         }
         catch (MQException mqex)
         {
            System.Console.Out.WriteLine("MQTest02 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
         }
         catch (System.IO.IOException ioex)
         {
            System.Console.Out.WriteLine("MQTest02 ioex=" + ioex);
         }
         finally
         {
            try
            {
               queue.Close();
               System.Console.Out.WriteLine("MQTest02 closed: " + outputQName);
            }
            catch (MQException mqex)
            {
               System.Console.Out.WriteLine("MQTest02 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
            }
            try
            {
               qMgr.Disconnect();
               System.Console.Out.WriteLine("MQTest02 disconnected from " + qManager);
            }
            catch (MQException mqex)
            {
               System.Console.Out.WriteLine("MQTest02 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
            }
         }
      }

      /// <summary> main line</summary>
      /// <param name="args">
      /// </param>
      //        [STAThread]
      public static void Main(System.String[] args)
      {
         MQTest02 mqt = new MQTest02();

         try
         {
            mqt.init(args);
            mqt.testReceive();
         }
         catch (System.ArgumentException e)
         {
            System.Console.Out.WriteLine("Usage: MQTest02 -h host -p port -c channel -m QueueManagerName -q QueueName [-u userID] [-x passwd] [-s securityExit]");
            System.Environment.Exit(1);
         }
         catch (MQException e)
         {
            System.Console.Out.WriteLine(e);
            System.Environment.Exit(1);
         }

         System.Environment.Exit(0);
      }
   }
}

要使用您的信息运行 MQTest02,它将是:

MQTest02.exe -h A1PATA00.WORLDSPAN.COM -p 1414 -m A1PATA00 -c ECACHE -q SPLASH.ECAC.2

【讨论】:

  • 这个答案所暗示的多线程优势是关于为什么方法 A 优于方法 B 的重要细节。示例 A 中创建的对象可以在传递下一个消息实例时传递给线程转到另一个线程。示例 B ClearMessages 要求您在重用之前完全处理该消息。
【解决方案2】:

在 IBM MQ 知识中心页面“ClearMessage method”中记录了以下内容:

此方法清除MQMessage 对象的数据缓冲区部分。 数据缓冲区中的任何消息数据都会丢失,因为 MessageLength, DataLength 和 DataOffset 都设置为零。


消息描述符 (MQMD) 部分不受影响; 应用程序 在重用之前可能需要修改一些 MQMD 字段 MQMessage 对象。 要重新设置 MQMD 字段,请使用 New 替换 具有新实例的对象。

在 IBM MQ 知识中心页面“MQMessage .NET class”中记录了以下内容:

公共字节[] MessageId {get;设置;}

对于 MQQueue.Get 调用,此字段指定消息标识符 要检索的消息。 通常,队列管理器返回 具有消息标识符和相关标识符的第一条消息 匹配消息描述符字段。 允许任何消息标识符 使用特殊值 MQC.MQMI_NONE 进行匹配

公共字节[] CorrelationId {get;设置;}

对于 MQQueue.Get 调用,消息的相关标识符到 队列管理器返回第一条带有 消息标识符和与消息匹配的相关标识符 描述符字段。 默认值 MQC.MQCI_NONE 有助于任何 要匹配的相关标识符。

试试这个:

            var queueMessage = new MQMessage();  <--- i want to do this new once!
            while (true) {
                //var queueMessage = new MQMessage(); // <-- only works if I do this every time i do a get
                queueMessage.ClearMessage();
                queueMessage.MessageId = MQC.MQMI_NONE;
                queueMessage.CorrelationId = MQC.MQCI_NONE; 
                get.Get(queueMessage, gmo);

【讨论】:

  • 我已经试过了。永远阻塞 Get() 。你会认为这会起作用。
  • 我刚刚在堆栈溢出时发现了这个。 stackoverflow.com/questions/18027043/…。他们还清除了相关 ID。我要测试一下。
  • 是的。 queueMessage.MessageId = MQC.MQMI_NONE; queueMessage.CorrelationId = MQC.MQCI_NONE;成功了。把它放在你的答案中,然后给你。
  • @johnnycrash 基于文档,它试图通过先前的 MessageIdCorrelationId 获取另一条消息,基于您当前的 WaitInterval,如果您等待 166.66分钟(10,000 秒)您最终会收到 RC 2033 (MQRC_NO_MSG_AVAILABLE)
  • 信用stackoverflow.com/a/18220744/121540 有一点帮助。
猜你喜欢
  • 2021-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多