WebSphere MQ / C#subscribe:主题GET永久挂起

时间:2017-10-31 20:11:26

标签: c# ibm-mq

  1. 我正在尝试在C#/ .Net应用程序中实现MQ发布/订阅。

  2. 我按照本教程中的说明操作:

    https://tekslate.com/publish-subscribe-in-websphere-mq-series/

    • a)队列经理:QM
    • b)TCP侦听器正在运行,端口:1420
    • c)主题名称:NEWS.SPORTS.CRICKET
    • d)订阅名称:NEWS.SPORTS.CRICKET
    • e)主题字符串:NEW / SPORTS / CRICKET
    • f)目的地队列:SportsQ
  3. 我成功地在MQ Explorer中进行了“测试发布”。 我在订阅中看到“消息计数= 1”。 我在SportsQ

  4. 中看到“队列深度= 1”
  5. 我能够连接到QM,我可以访问该主题......但是当我执行“topic.Get(message”)

  6. 时它就会挂起

    问:为什么MQ“Get()”挂起?????

    代码:

    using IBM.WMQ;
    using System;
    using System.Collections;
    
    namespace HelloSubscribe
    {
        class Program
        {
            static void Main(string[] args)
            {
                string qmName = "QM";
                string hostName = "localhost";
                string strPort = "1420";
                string channelName = "SYSTEM.DEF.SVRCONN";
                string transport = MQC.TRANSPORT_MQSERIES_CLIENT;
    
                Hashtable connectionProperties = new Hashtable();
                connectionProperties.Add(MQC.HOST_NAME_PROPERTY, hostName);
                connectionProperties.Add(MQC.PORT_PROPERTY, strPort);
                connectionProperties.Add(MQC.CHANNEL_PROPERTY, channelName);
    
                MQQueueManager mqQueueManager = new MQQueueManager(qmName, connectionProperties);
    
                string topicObject = null;
                string topicString = "NEWS/SPORTS/CRICKET";
                string subscriptionName = "NEWS.SPORTS.CRICKET";
                string topicName = "NEWS.SPORTS.CRICKET";
                int openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_DURABLE | MQC.MQSO_RESUME;
                MQTopic destForGet = mqQueueManager.AccessTopic(topicString, null, openOptionsForGet, null, subscriptionName);
    
                MQMessage messageForGet = new MQMessage();
                MQGetMessageOptions gmo = new MQGetMessageOptions();
                gmo.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING;
                gmo.WaitInterval = 1000;  // wait 60 seconds
                destForGet.Get(messageForGet, gmo);
                string msg = messageForGet.ReadLine();
    
                destForGet.Close();
                mqQueueManager.Disconnect();
                mqQueueManager.Close();
            }
        }
    }
    

    我正在使用已安装的amqdnet.dll版本和Visual Studio 2015运行WebSphere MQ 7.5。

2 个答案:

答案 0 :(得分:3)

MQ管理订阅

您引用的教程是您设置MQ管理订阅以将主题字符串订阅到特定的MQ队列,这样,发布到与订阅的主题字符串匹配的主题的任何消息都将被放入您在MQ上指定的队列中例如SportsQ的管理订阅,要从队列中读取消息,您将其视为队列而不是主题,并使用AccessQueue方法访问它,例如:

        string queueName = "SportsQ";
        int openOptionsForGet = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_FAIL_IF_QUIESCING;
        MQQueue destForGet = mqQueueManager.AccessQueue(queueName, openOptionsForGet, null, null, null);

托管持久订阅

您在所提供的代码中演示的内容称为托管持久订阅,后台MQ将创建一个前缀为SYSTEM.MANAGED.DURABLE.<uniq 16 character HEX value>的队列,并将其订阅到主题字符串。如果您断开连接,然后使用相同的&#34;订阅名称&#34;然后,您将连接到同一个托管队列,并接收未连接时发布的任何消息。

请注意,在称为保留发布的内容之外,MQ将为将来的订阅者保存最新的发布,在订阅之前,您不会收到发布到主题字符串的任何消息,这就是为什么您不会看到消息的原因在创建托管持久订阅之前通过MQ Explorer发布。

根据您提供的内容,您应该只能向主题字符串发布新消息,您的应用程序就会收到该消息。

非托管持久订阅

另一种选择是删除您创建的MQ管理订阅并更改您的程序以在您提供队列目标的位置进行非托管订阅,例如:

        string queueName = "SportsQ";
        int QueueOpenOptionsForGet = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_FAIL_IF_QUIESCING;
        int TopicOpenOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_DURABLE | MQC.MQSO_RESUME;
        MQQueue destQueue = mqQueueManager.AccessQueue(queueName, QueueOpenOptionsForGet);
        MQTopic destForGet = mqQueueManager.AccessTopic(destQueue, topicString, null, openOptionsForGet, null, subscriptionName);

这会导致创建一个API订阅,将主题字符串链接到您提供的队列作为目标。

就像使用MQ管理订阅一样,您可以将此作为一个队列来处理,或者您可以恢复订阅。

在不需要知道队列名称的情况下恢复非托管持久订阅

您仍然需要调用具有目标作为第一个参数的AccessTopic重载,但是传入NULL而不是MQDestination,程序将恢复已创建的非托管订阅,而无需知道队列名称。我能够编译和测试以下工作:

using IBM.WMQ;
using System;
using System.Collections;


namespace HelloSubscribe
{
    class Program
    {
        static void Main(string[] args)
        {
            string qmName = "QM";
            string hostName = "localhost";
            string strPort = "1420";
            string channelName = "SYSTEM.DEF.SVRCONN";
            string transport = MQC.TRANSPORT_MQSERIES_CLIENT;

            Hashtable connectionProperties = new Hashtable();
            connectionProperties.Add(MQC.HOST_NAME_PROPERTY, hostName);
            connectionProperties.Add(MQC.PORT_PROPERTY, strPort);
            connectionProperties.Add(MQC.CHANNEL_PROPERTY, channelName);

            MQQueueManager mqQueueManager = new MQQueueManager(qmName, connectionProperties);

            string topicString = "NEWS/SPORTS/CRICKET";
            string subscriptionName = "NEWS.SPORTS.CRICKET";
            int openOptionsForGet = MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_DURABLE | MQC.MQSO_RESUME;
            MQTopic destForGet = mqQueueManager.AccessTopic(null, topicString, null, openOptionsForGet, null, subscriptionName);

            MQMessage messageForGet = new MQMessage();
            MQGetMessageOptions gmo = new MQGetMessageOptions();
            gmo.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING;
            gmo.WaitInterval = 60000;  // wait 60 seconds
            destForGet.Get(messageForGet, gmo);
            string msg = messageForGet.ReadLine();
            System.Console.WriteLine("Received message data : " + msg);

            destForGet.Close();
            mqQueueManager.Disconnect();
            mqQueueManager.Close();
        }
    }
}

另请注意,您甚至不需要topicString,也可以为null,只需要subscriptionName即可恢复它:

            MQTopic destForGet = mqQueueManager.AccessTopic(null, null, null, openOptionsForGet, null, subscriptionName);

其他一些注释,本教程中的示例提供了混合大小写的队列名称,建议不要这样做,因为在许多地方,如果您不小心,IBM MQ会将内容折叠为UPPER CASE。建议的最佳实践是对IBM MQ对象名称(队列等)使用UPPER CASE。

WaitInterval以毫秒为单位,因此将其设置为1000将等待1秒而不是60秒。

答案 1 :(得分:2)

我通过指定目标队列来实现它:

    ...
    string topicString = "NEWS/SPORTS/CRICKET";
    string subscriptionName = "NEWS.SPORTS.CRICKET";
    string topicName = "NEWS.SPORTS.CRICKET";
    MQDestination unmanagedDest = mqQueueManager.AccessQueue("SportsQ", MQC.MQOO_INPUT_EXCLUSIVE | MQC.MQOO_FAIL_IF_QUIESCING);

    int openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_DURABLE | MQC.MQSO_RESUME;
    // MQTopic destForGet = mqQueueManager.AccessTopic(topicName, null, openOptionsForGet, null, subscriptionName);  // Hangs in "Get()"
    // MQTopic destForGet = mqQueueManager.AccessTopic(topicName, null, MQC.MQTOPIC_OPEN_AS_SUBSCRIPTION, openOptionsForGet);  // MQRC_SUB_NAME_ERROR
    // MQTopic destForGet = mqQueueManager.AccessTopic(topicName, topicString, MQC.MQTOPIC_OPEN_AS_SUBSCRIPTION, openOptionsForGet);  // MQRC_SUB_NAME_ERROR
    // MQTopic destForGet = mqQueueManager.AccessTopic(topicName, topicString, openOptionsForGet, null, subscriptionName);  // Hangs in "Get()"
    // MQTopic destForGet = mqQueueManager.AccessTopic(topicName, "SportsQ", openOptionsForGet, null, subscriptionName);  // Hangs
    // MQTopic destForGet = mqQueueManager.AccessTopic(topicName, "SportsQ", MQC.MQTOPIC_OPEN_AS_SUBSCRIPTION, openOptionsForGet);  //  MQRC_SUB_NAME_ERROR
    // MQTopic destForGet = mqQueueManager.AccessTopic(unmanagedDest, topicName, null, openOptionsForGet);  // MQRC_SUB_NAME_ERROR
    MQTopic destForGet = mqQueueManager.AccessTopic(unmanagedDest, topicName, null, openOptionsForGet, null, subscriptionName);  // <-- this works!

    MQMessage messageForGet = new MQMessage();
    MQGetMessageOptions gmo = new MQGetMessageOptions();
    gmo.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING;
    gmo.WaitInterval = 1000;  // wait 60 seconds
    destForGet.Get(messageForGet, gmo);  // <-- No hang, if message present
    string msg = messageForGet.ReadLine();
    ...