【问题标题】:Sending a message using IBM MQ, getting 2059 error or hang on construction of Queue Manager使用 IBM MQ 发送消息,收到 2059 错误或挂起队列管理器的构建
【发布时间】:2017-11-15 23:32:29
【问题描述】:

我的任务是向(目前)未知的 MQ 端点发送测试消息。

我在服务器上安装了 IBM WebSphere MQ (v8.0.0.5) 的试用版,我相信配置正确。

但是,鉴于下面包含的代码,我得到了异常:

“IBM.WMQ.MQException”类型的未处理异常发生在 amqmdnet.dll

附加信息:2059

或者,如果我在服务器本身上使用localhost 而不是远程服务器名称,则构造函数行挂起。

这是 C#:

Hashtable connectionProperties = new Hashtable();

string connectionType = MQC.TRANSPORT_MQSERIES_MANAGED
connectionProperties.Add(MQC.TRANSPORT_PROPERTY, connectionType);

// Set up the rest of the connection properties, based on the
// connection type requested
switch (_connectionType)
{
    case MQC.TRANSPORT_MQSERIES_BINDINGS:
        break;
    case MQC.TRANSPORT_MQSERIES_CLIENT:
    case MQC.TRANSPORT_MQSERIES_XACLIENT:
    case MQC.TRANSPORT_MQSERIES_MANAGED:
        connectionProperties.Add(MQC.HOST_NAME_PROPERTY, "server.com");
        connectionProperties.Add(MQC.CHANNEL_PROPERTY, "SYSTEM.DEF.CLNTCONN");      
        break;
}

MQQueueManager qMgr = new MQQueueManager("test", connectionProperties);
MqClientTest mqClientTest=new MqClientTest("TEST_QM","localhost", "SYSTEM.DEF.CLNTCONN");

我们有什么遗漏的吗?


更新 1:

errors 文件夹的amqerr01.log 文件中,我们现在有了文本:

AMQ6183:发生内部 WebSphere MQ 错误。

更新 2:

“内部 WebSphere MQ 错误”可能是由于我探测 1414 端口以查看该端口是否“启动”并正在侦听。似乎是,而且它显然不喜欢我这样做。

更新 3:

@Roger 建议我使用 127.0.0.1 和大写字母,我现在已经应用了 - 并更改了上面的示例以反映。

@JoshMc 好心地指向队列管理器文件夹中的第二个 AMQERR01.LOG 文件。我现在得到错误:

14/11/2017 15:35:08 - 进程 (336.6) 用户 (xxxx) 程序(amqrmppa.exe) 主机(xxxx) 安装(xxxx) VRMF(8.0.0.5) QMgr(TEST_QM) AMQ9519:未找到通道“SYSTEM.DEF.CLNTCONN”。

解释:请求的操作失败,因为程序可以 找不到频道“SYSTEM.DEF.CLNTCONN”的定义。

TEST_QM 是我的队列管理器,默认 (?) 频道 SYSTEM.DEF.CLNTCONN

【问题讨论】:

  • 看起来你还没有使用这个端口。如果您不指定端口号,那么它将采用默认端口,即 1414。
  • 是的,正如你所说,我们应该默认使用 1414。花了最长的时间来理解为什么我们没有在 1414 上收听(使用netstat -a)然后我们找到了一个监听器。所以,我们现在正在收听 1414。
  • 1.您指定的频道名称是什么?如果它是以SYSTEM. 开头的频道,那么默认的CHLAUTH 规则将阻止它。 2. 如果您没有被该规则阻止,因为您正在使用不同的频道或禁用 CHLAUTH,那么您连接的是哪个用户,如果它是 mqm 组中的用户或假设您在 Windows 上,任何windows管理员组中的用户,或MUSR_MQADMIN用户,MQ v8默认配置为要求所有管理用户通过CONNAUTH指定密码。
  • 注意有两个AMQERR01.LOG文件,一个位于MQ安装位置正下方的errors目录中,另一个位于MQ安装位置下的qmgrs//errors目录下,你看过第二个位置了吗?
  • FDC 文件将在安装目录而不是队列管理器目录下的错误目录中创建。如果探测器只是简单地连接和断开连接,那么通常 MQ 将不再记录任何内容。如果探针连接并且一些意外的数据被发送到 MQ,那么它通常会生成一个 FDC 文件。如果您知道它对应于端口探测,则可以放心忽略。如果它生成太多数据,甚至有办法告诉 MQ 禁止产生这些错误。

标签: c# ibm-mq


【解决方案1】:

对于 IBM MQ,总是有一对协同工作的通道。例如,如果一个 MQ 队列管理器需要将消息发送到另一个队列管理器,它通常会定义一个 SDR(发送者)通道,该通道将指向另一个队列上具有相同名称的 RCVR(接收者)通道经理。

对于 MQ 客户端连接,通道 的客户端应用程序端称为MQCD,MQ 队列管理器端称为SVRCONN(服务器连接)通道。客户端MQCD 可以通过几种不同的方式指定。您在发布的示例中演示的方式是以编程方式指定 MQCD 信息(频道名称、主机名等)。

另一种指定信息的方法是指向通常称为通道表的CCDT(客户端通道定义表),这是使用CLNTCONN 通道的地方,因为您不是使用频道表,我将继续说明您收到错误的原因,但在此答案的末尾,我将提供有关频道表的更多详细信息。


总的来说,您的应用程序不应该CLNTCONN 频道指定为MQC.CHANNEL_PROPERTY,它应该指向SVRCONN 频道。

需要注意的两点:

  1. 您不应该为此目的使用预定义的SYSTEM.* 通道(例如SYSTEM.DEF.SVRCONN),您应该定义一个新通道供您的应用程序使用。默认通道用于保存您希望在创建相同类型的新通道时成为默认值的参数。
  2. MQ v7.1 及更高版本默认启用了一些CHLAUTH 规则,这些规则将阻止对SYSTEM.* 通道的访问。

如果您想在 Windows 上使用命令行程序 runmqsc 定义 SVRCONN 频道,请在 CMD 提示符下运行以下命令:

echo DEFINE CHL(TEST_QM_CHANNEL1) CHLTYPE(SVRCONN)|runmqsc TEST_QM

关于通道表的更多细节:

在 MQ v8 之前,唯一受支持的生成通道表的方法是在队列管理器上定义 CLNTCONN 通道,这会在名为 @ipcc 的目录中的队列管理器名称文件夹下创建一个名为 AMQCLCHL.TAB 的文件。队列管理器附带一个名为SYSTEM.DEF.CLNTCONN 的通道的原因是在您创建新的CLNTCONN 通道时提供默认值。使用 v8 及更高版本,您可以仅使用 runmqsc -n 模式创建通道表,这将直接编辑通道表而不需要在队列管理器上创建 CLNTCONN 通道。 v8 及更高版本的 MQ 客户端现在附带了一个客户端版本 runmqsc,可用于创建和更改通道表。

应用程序可以通过几种不同的方式指向通道表文件,一种常见的方式是设置两个环境变量(MQCHLLIBMQCHLTAB)。应用程序然后在程序中不指定通道名、主机名等,只需要指定要连接的队列管理器的名称。

MQ 查找连接信息的顺序和位置记录在 IBM v7.5(或更高版本)知识中心页面“Connecting IBM WebSphere MQ MQI client applications to queue managers”中。请注意,此链接适用于 MQI 客户端,但相同的基本顺序也适用于 .NET 客户端。

【讨论】:

  • 感谢您的详细描述。我们只在 IBM WebSphere MQ Explorer 应用程序中应用我们的配置。我同意你关于避免使用系统频道的观点。从那以后,我创建了一个频道TEST_QM_CHANNEL,结果相似(或没有结果)。这应该是“客户端通道”吗?我们需要在通道上设置什么设置才能将消息接收到队列中吗?
  • 感谢您抽出宝贵时间。我已经删除了客户端通道并使用 GUI 创建了一个“服务器连接”通道。我已经验证类型是“服务器连接”。我现在没有时间尝试你的命令行(我会),因为我有一个会议。但是我们现在在AMQLOG 文件中有一个新错误:检查了用户 ID '(user)' 及其密码,因为用户 ID 是特权用户,并且队列管理器连接权限 (CONNAUTH) 配置引用了身份验证信息 (AUTHINFO ) 名为 'SYSTEM.DEFAULT.AUTHINFO.IDPWOS' 的对象,带有 CHCKCLNT(REQDADM)。进步!
  • @Program.Xp 请参阅我对您的问题的评论中的#2 com 11 月 10 日 16:24。
  • @Program.X 您链接到的技术说明与连接到 MQ 的 Websphere Application Server 有关,这将是用于 JMS 应用程序的 IBM MQ 类,它也仅涵盖 MQ v7 附带的 WAS 8.5 .1 并且不包括在 MQ v8 中添加的增强功能,以支持来自 JMS 应用程序的更长用户名。 C# 还支持更长的用户名。请参阅我对问题“Connecting from .NET to MQ-Series”的回答
  • 请注意,即使您发送管理员 ID 和密码,您也可能会被另一个阻止管理员 ID 连接到 SVRCONN 频道的 CHLAUTH 规则阻止。最佳实践是设置一个专用于将要连接的应用程序的低权限用户 ID,该 ID 可以被授权给它需要访问的一个或多个队列。如果您以管理员身份连接,则您可以访问整个队列管理器。
【解决方案2】:

本地主机

尝试使用 127.0.0.1 而不是 localhost。

“rx”和“swift_test_tx”

由于您是 MQ 的新手,现在是您阅读 MQ 最佳实践的时候了。一项是对 MQ 对象名称使用小写字符不是一个好主意。即队列、通道等...... MQ 将始终将任何不在引号中的名称折叠为大写。因此,最好以大写开头的名称(将来问题更少)。

接下来,您是否在防火墙中打开了 1414 端口?如果没有,则打开 TCP 和 UDP 的端口。

这是一个将消息放入远程队列管理器中的队列的 C# 程序示例:

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

/// <summary> Program Name
/// MQTest51
///
/// Description
/// This C# class will connect to a remote queue manager
/// and put a message to a queue under a managed .NET environment.
///
/// Sample Command Line Parameters
/// -h 127.0.0.1 -p 1414 -c TEST.CHL -m MQWT1 -q TEST.Q1
///
/// </summary>
namespace MQTest51
{
   class MQTest51
   {
      private Hashtable inParms = null;
      private Hashtable qMgrProp = null;
      private System.String qManager;
      private System.String outputQName;

      /*
      * The constructor
      */
      public MQTest51()
         : 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("-u")) && (inParms.ContainsKey("-x")) )
               qMgrProp.Add(MQC.USE_MQCSP_AUTHENTICATION_PROPERTY, true);
         }
         else
         {
            throw new System.ArgumentException();
         }
      }

      /// <summary> Connect, open queue, write a message, close queue and disconnect.
      ///
      /// </summary>
      /// <throws>  MQException </throws>
      private void testSend()
      {
         System.String line;
         int openOptions = MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING;

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

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

            MQPutMessageOptions pmo = new MQPutMessageOptions();

            // Define a simple MQ message, and write some text in UTF format..
            MQMessage sendmsg = new MQMessage();
            sendmsg.Format = MQC.MQFMT_STRING;
            sendmsg.Feedback = MQC.MQFB_NONE;
            sendmsg.MessageType = MQC.MQMT_DATAGRAM;

            line = "This is a test message embedded in the MQTest51 program.";

            sendmsg.MessageId = MQC.MQMI_NONE;
            sendmsg.CorrelationId = MQC.MQCI_NONE;
            sendmsg.WriteString(line);

            // put the message on the queue

            queue.Put(sendmsg, pmo);
            System.Console.Out.WriteLine("Message Data>>>" + line);

            queue.Close();
            System.Console.Out.WriteLine("MQTest51 closed: " + outputQName);
            _qMgr.Disconnect();
            System.Console.Out.WriteLine("MQTest51 disconnected from " + qManager);
         }
         catch (MQException mqex)
         {
            System.Console.Out.WriteLine("MQTest51 cc=" + mqex.CompletionCode + " : rc=" + mqex.ReasonCode);
         }
         catch (System.IO.IOException ioex)
         {
            System.Console.Out.WriteLine("MQTest51 ioex=" + ioex);
         }
      }

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

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

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

【讨论】:

  • "如果没有,则打开 TCP 和 UDP 的端口。"为什么选择 UDP?
  • 根据@MoragHughson 对问题“Is it possible to make websphere MQ work with UDP protocol?”的回答,UDP 仅在 AIX 上提供,并且“它在几个版本前被删除。”,答案来自 3 年前。我在 v7.0.1 知识中心找不到对它的引用。
  • 谢谢@Roger。这就是我所缺少的那种洞察力。我已将所有内容重命名为 CAPS,因此删除了旧的 QueueManager 并创建了一个新的“TEST_QM”。但是,我们得到相同的 2059 结果。
  • 您确定您使用的端口号正确吗?检查您的队列管理器日志文件,您可能被安全阻止了。
猜你喜欢
  • 2017-11-08
  • 1970-01-01
  • 2016-04-08
  • 2021-01-03
  • 1970-01-01
  • 2021-06-15
  • 2017-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多