【问题标题】:WebSphere MQ/C# subscribe: topic GET hangs foreverWebSphere MQ/C# 订阅:主题 GET 永远挂起
【发布时间】:2017-10-31 20:11:25
【问题描述】:
  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 中看到“队列深度 = 1”

  4. 我能够连接到 QM,我能够访问主题...但是当我执行“topic.Get(message”) 时它只是挂起

问:为什么 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();
        }
    }
}

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

【问题讨论】:

  • 您使用什么标准来确定消息的结尾?您有一个 readline,所以您是否在发送的消息末尾添加了一个返回? readline 方法将阻塞,直到收到返回。
  • 我刚刚在 MQ 资源管理器中进行了“测试发布”并输入了一些文本。根据我阅读和编写点对点消息的经验,ReadLine() 应该是合适的。问题是我从来没有走到那一步——它挂在“Get()”中。
  • 是的,是的,是的!!!! Get 也将阻塞,直到消息结束。您没有发布 Get 方法,所以我不知道 Get 如何确定结束。
  • 尝试一些更简单的方法来确保所有库都正确安装。在以下网页上尝试代码:c-sharpcorner.com/article/….
  • 看起来我在 2015 年帮助过有类似问题的人:social.msdn.microsoft.com/Forums/windowsdesktop/en-US/…

标签: c# ibm-mq


【解决方案1】:

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> 的队列并将其订阅到主题字符串。如果您断开连接然后使用相同的“订阅名称”恢复,那么您将连接到同一个托管队列并接收在您未连接时发布的任何消息。

请注意,在称为保留发布的东西之外,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 在许多地方会将内容折叠为大写。推荐的最佳实践是对 IBM MQ 对象名称(队列等)使用大写。

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

【讨论】:

  • 谢谢。我实际上是在尝试创建一个可以在 C# 中读取的非托管、持久订阅。当我创建订阅时,MQ 资源管理器想要在“目标”中添加一些东西。我认为“SportsQ”(根据教程)会很好。问:如果不是队列,我应该为“目的地”指定什么?对于非托管/持久订阅?
  • 不在我的电脑上。您现在拥有的是我回答的第一部分。你想要的是我回答的第三部分
  • 我在我的问题“无需知道队列名称的情况下恢复非托管持久订阅”中添加了一个新部分
  • @paulsm4 我更新了“在不需要知道队列名称的情况下恢复非托管持久订阅”部分,这是你的原始代码,我建议将 null 作为目标传递,我能够编译它并查看它在预先存在的管理定义的非托管订阅上获取消息。
  • @paulsm4 我能够使用以下方法恢复管理定义的非托管订阅:MQTopic destForGet = mqQueueManager.AccessTopic(topicString, null, openOptionsForGet, null, subscriptionName);。只要topicStringsubscriptionName 与您通过MQ Explorer 指定的值匹配,它就会恢复它。你能确认你使用的是完整的 MQ 版本吗,我用 7.5.0.5 测试过。
【解决方案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();
    ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多