MQ异常 关闭原因 = 2009[个人收藏 他人求助及回答]

来源:互联网 发布:2016淘宝618 编辑:程序博客网 时间:2024/05/27 16:43

1#-

请教各位大虾:
客户端连接到队列管理器所在的服务器进行消息接收时,如果长时间该队列中一直没有消息可以取,但是客户接收程序还继续运行,过一段时间MQ就会报如下异常:

com.ibm.mqservices.MQInternalException:MQJE001:发生 MQException:完成码 2,原因码 2009
MQJE016:连接期间,MQ 队列管理器立即关闭通道
关闭原因 = 2009

导致队列管理器的消息通道关闭,发送方无法再放置消息,除非将导致该通道关闭异常的客户端接收接收程序关闭,这样队列管理器就恢复正常了,但是我还不想让接收程序关闭,不知道怎样避免这种情况的发生,有什么解决方案?导致这种情况的原因是什么?

我的队列管理器是建在UNIX环境下,只定义了一个消息通道类型为SVRCONN:
DEFINECHANNEL(S_QM_APSIS_TEST) CHLTYPE(SVRCONN)  MAXMSGL(104857600) REPLACE 
采用的通讯方式为将消息放置到Server端的本地队列中,客户端从到该Server端的本地队列中取得消息;当客户端需要返回消息给服务器端时,客户端发往服务器端的队列,不知道我的这个通道就是属于MQI通道类型?我看网上好多资料都是建立多个通道:发送方,接收方和服务器端通道,不知道这种通道什么情况下使用,对通道的类型及每种类型什么情况下使用不是很清楚,还望大家多多帮忙指教,谢谢!!:)

 

2#-

大家不要光看不发表意见啊!知道的仁兄帮帮忙啊!!好急的,不要见死不救啊!!
为了方便找出导致通道关闭的原因,现将接收消息的代码贴出来,如下:

importorg.dom4j.Document;

importcom.ibm.mq.*;

publicclass RSVMonitorIndexReturnAction extends Thread {

  /**
  * @function   以多线程的方式接收信息<br>
  * @author     hidden_wing<br>
  */

  publicvoid run() {
   Document doc = null;
   try {
           String qName = QL;
     //设置MQ的环境变量
     MQEnvironment.hostname = 10.6.12.33;
     MQEnvironment.channel = S_QM;
     MQEnvironment.CCSID = 819;
     MQEnvironment.port = 1414;

     MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
                                MQC.TRANSPORT_MQSERIES_CLIENT);

     // 连接到队列管理器
     MQQueueManager qMgr = new MQQueueManager(qManager);

     // 设置打开属性
     int openOptions = MQC.MQOO_INPUT_SHARED
                      | MQC.MQOO_FAIL_IF_QUIESCING;

     // 打开队列
     MQQueue queue = qMgr.accessQueue(qName, openOptions, null,null,
                                    null);

     // 从消息读取用户数据
     MQMessage msg = null;

     //获取队列中的所有消息
     while ((msg = fetchMsg(queue)) != null) {
       doc = (Document) msg.readObject();
       ......
     }

     // 提交事务
     qMgr.commit();
     // 关闭队列和队列管理器对象
     queue.close();
     qMgr.disconnect();

   } catch (MQException ex) {
     log.info("接收MQ出现错误:"+ ex.getMessage());
   } catch (Exception e) {
     if(e.getMessage()!=null){
       log.info("出现错误:"+ e.getMessage());
     }
   }
  }

  /**
  * @function   获取队列中的消息<br>
  * @author     hidden_wing<br>
  * @param      MQQueue     q    --定义一个MQQueue类型的对象来获取消息缓冲区中的消息
  */

  privateMQMessage fetchMsg(MQQueue q) throws Exception {
   MQGetMessageOptions mgo = new MQGetMessageOptions();
   mgo.options |= MQC.MQGMO_NO_WAIT;
   MQMessage msg = new MQMessage();
   try {
     //获取消息
     q.get(msg, mgo);
   } catch (MQException e) {
     return null;
   }
   return msg;
  }
}

说明:RSVMonitorIndexReturnAction 类是通过一个多线程的方式每隔6秒钟调用一次,在RSVMonitorIndexReturnAction 类中我通过一个while循环,来将队列中现有的消息一次取出,不知道这种实现方式在逻辑上是否有问题,是否存在一些没有释放的资源?

另外问个问题,q.get(msg, mgo)方法是调用一次就取一条消息吗?例如我的程序中消息是一个document对象,那么是不是每调用一次get方法就取出一个document对象,而不是多个?

 

3#-

下边是MQ服务器端记录的错误日志
AMQ9513: Maximum number of channels reached.

EXPLANATION:
The maximum number of channels that can be in use simultaneously has been
reached. The number of permitted channels is a configurable parameter in the
queue manager configuration file.
ACTION:
Wait for some of the operating channels to close. Retry the operation when some
channels are available.

 

4#--1#

问题比较多, 先回答后面提到的:

1. 你定义的通道类型为 SVRCONN 服务器连接通道,在WMQ 中称为MQI通道,是供应用程序以客户端的方式连接服务器使用的;
另外在 WMQ 中称为消息通道的是指服务器到服务器之间的连接(准确地说,是队列管理器和队列管理器之间),这时候,会有你提到的发送方,接收方和服务器端通道等类型。
详细的资料,最好参考下WMQ 的信息中心。
2, 从你上面的错误描述来看, 2009 的错误代码为CONNECTION_BROKEN (可以用命令 #mqrc 2009),这表示连接中断,但是连接中断的原因很多,通常为网络通信的原因,如果是这种情况,必须在程序中检查到这样的返回值后,程序重新建立连接;如果程序不重新建立连接,就像你提到的一样,需要中断掉程序。一般,你可以查看 MQ 的 error log 来获得更多的信息,其中可能包含通信错误的原因( /var/mqm/errorsand  /var/mqm/YourQueueManager/errors 目录中), 你可能会看到如下表所列的 tcp/ip 的一些错误。

表 1. Unix TCP/IP errno。Errno  Errno 号码  描述  
AIX  HP-UX  Solaris  Linux  
EINTR  4  4  4  4  系统调用中断。  
EAGAIN  11  11  11  11  资源临时不可用。  
EBUSY  16  16  16  16  资源正忙。  
EMFILE  24  24  24  24  每进程文件描述符表已满。  
EPIPE  32  32  32  32  管道断开。  
EADDRINUSE  67  226  125  98  已经在使用指定的地址。  
ENETDOWN  69  228  127  100  网络已停止。  
ENETUNREACH  70  229  128  101  没有任何至网络的路由可用。  
ENETRESET  71  230  129  102  网络复位时删除了连接。  
ECONNRESET  73  232  131  104  伙伴已复位连接。  
ENOBUFS  74  233  132  105  系统中没有足够的缓冲区空间资源可用来完成调用。  
ENOTCONN  76  235  134  107  未连接套接字。  
ETIMEDOUT  78  238  145  110  连接超时。  
ECONNREFUSED  79  239  146  111  连接已被拒绝。如果您正在尝试与数据库相连,则检查是否已成功启动了服务器上的数据库管理器和 TCP/IP 协议支持。 
如果使用 SOCKS 协议支持,则还要确保在 SOCKS 服务器上已成功启动了 TCP/IP 协议支持。 

EHOSTDOWN  80  241  147  112  主机已当机。  
EHOSTUNREACH  81  242  148  113  没有任何至主机的可用路由。  

有关 Unix TCP/IP 通信错误的更多信息,请参阅适当操作系统的技术参考手册

 

5#--2#

一口气这么多问题,一个个来:
1. 程序中的不妥地方:
没有看到MQGetMessageOptions 中设定MQC.MQGMO_SYNCPOINT ,你的  
     // 提交事务
     qMgr.commit();
语句应当不能达到你的效果吧? 你本意是想把所有的消息放到一个事务中吗? 需要你检查程序!

2. 你是用的 mqjava base class 接口, 其实你的多线程以及while 循环达到的效果, 是否可以考虑使用 mq 的 get 消息时,加上时间等待的方式(通常称为 blockread ) 的方式? 这种设计是否可以适应你的需求呢? 因为这样在 block 的时候不需要耗用 cpu, 不像你这样子轮循对 cpu 耗用很大; 另外, 也不会发生,刚好消息到的时候,你却没有读走,只能靠下次才读走的问题。

3. 一次 get 操作,当然只能读取一条消息了, 这是最基本的单元操作。

 

6#--3#

不知道你看到的这个信息是不是服务器端唯一的错误? 也不知道是不是你测试过,明确对应的你客户段程序发生问题时候,看到的错误,  我猜测不是,需要你继续查看其它更多的信息。

关于这个信息出现后,解决的办法为:

调整服务器通道的最大连接数等参数,unix 上是通过修改队列管理器的 qm.ini 文件,详细的信息和含义参考如下连接:
http://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp
(System Admin/Configuring WebSphereMQ/Configuring WebSphere MQ /Changing queue manager configuration information/Channels )
对应中文内容为:
通道
CHANNELS节(在文件 qm.ini 中)来指定关于通道的信息。 
MaxChannels=100|number 
允许的通道最大数。缺省值是 100。 
MaxActiveChannels=MaxChannels_value 
允许在任何时候都活动的通道的最大数。缺省值是在 MaxChannels 属性上指定的值。 


阅读后基本步骤:
1.stop QueueManager
2.tune or add the channel paramaters within channel section of qm.ini
  (  MaxChannels和 MaxActiveChannels具体设置多大,需要参考你总共到服务器的并发连接实例,比如,统一时间,最大并发建立的队列管理器连接数为 500)
3.start QueueManager,
4. ok,

qm.ini样例: 
http://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp
(System Admin/Configuring WebSphereMQ/Configuring WebSphere MQ /Changing configuration information on UNIXsystems/ Queue manager configuration files, qm.ini )
相关片断如下:

Channels:
  MaxChannels=500
  MaxActiveChannels=500
  MQIBindType=STANDARD

 

7#-

最后的要求 to hidden_wing:

看在我一口气回复的面子上,你去
http://www-306.ibm.com/software/... rary/library6x.html
这里,下载一下 MQ v6.0 的信息中心(infoCenter), 在其中,能找到所有的你上面提到的关心的内容, 工具书总的有呀, 不然,哭得时候,只能自己面向墙角, 呵呵。

不下载,可对不起我,呵呵

 

8#-

还有问题需要向大家请教:
1、出现通道关闭2009这个异常时,就有一个客户端连接到队列管理器进行消息接收,有消息接收时还正常,长时间没有消息接收时就报这个错误,所以错误日志里记录了通道达到了最大数这个错误,但是就是一个客户端连接啊,怎么会默认的100通道数都不够,所以我怀疑是接收程序有问题,是不是用过的连接没有关闭或者释放?所以发上来让大家帮忙找找错。

2、既然我的通道是MQI类型的通道,它是一个真是存在的通道吗?我看材料说MQI不是一个通道,类似一个监听,当有客户端程序连接到MQ队列管理器进行消息通讯时才建立一个真实的通道,这种说法对吗?通道到底什么时候建立,是当程序new MQQueueManager()时就建立一个吗?还是说一个队列对应一个通道?建立后什么时候该通道的链接消失?

3、我怎么通过利用MQ本身的机制可以做到,队列中有消息的时候就触发接收程序进行消息接收?而不是通过定时轮循的方式?如果有例子给个例子最好了

 

9#--8#

#######################
问题1 的回复:
首先,在你的程序里没看到,主动重建连接的动作, 建议你在 qm.ini  文件中,channel段里加上下面的设定,看看结果如何,需要做的动作和前面调整最大数一样。
AdoptNewMCA=ALL

也即: 
Channels:
MaxChannels=500
MaxActiveChannels=500
AdoptNewMCA=ALL
MQIBindType=STANDARD

记得要重新启动队列管理器

#######################
问题2 的回复:
这个问题好,在 runmqsc 命令里, 你用 dis chs(*) 可以看到你的MQI 通道在有应用程序连接后实际启动的实例数,也就是当前活动的通道个数;  可以看出,在产品内部,MQI 就是一种通道(clientto server) ,和消息通道(server to server) 一样的。
通道建立的时间,你可以通过自己测试启动你的程序,同时 用 dis chs(*) or dis chs(Your Channel Name) 来监控,就可以知道,是在建立到队列管理器的连接时,建立的。

至于,通过通道连接时,不管 消息通道,MQI 通道,读需要有 监听器listener 的定义并启动的配合,不然你让别人怎么通过 TCP/IP找到你呀。

队列和通道时两个独立的概念,通道只是在涉及到分布式网络存取
的时候,才需要; 但是队列是你使用 mq 的基础,一定需要;


#######################
问题3 的回复:

通过使用 mq 的触发机制,就可以,
在队列上定义触发(first, deepth, everyone) ,然后触发你的程序就可以,程序几乎可以不用调整,当然,你要确定,程序被触发启动后,是作为 deamon 的方式一直运行,还是读取消息后,就退出结束,等待下一次触发后再被调用启动。

对应资料:
http://publib.boulder.ibm.com/infocenter/wmqv6/v6r0/index.jsp
(System Admin/Administration using WebSphere MQcommands /Administering local WebSphere MQ objects /Managing objects fortriggering ) 你也可以在下载后的信息中心里找到对应的中文资料

 

10#-

一个简单的trigger 例子: 
1. 定义队列上的trigger:

DEFINE QLOCAL (Q1) +
       PROCESS (P1) +
       INITQ (INITQ.Q1) +
       TRIGGER +
       TRIGTYPE (FIRST) 
或者

ALTER QLOCAL (Q1) +
       PROCESS (P1) +
       INITQ (INITQ.Q1) +
       TRIGGER +
       TRIGTYPE (FIRST) 

2. 定义Q1 trigger 中要用到的 INIT Queue
DEFINE QLOCAL (INITQ.Q1) 

3. 定义Q1 trigger 中要用到的 Process
DEFINE PROCESS (P1) +
               APPLTYPE(UNIX) +
               APPLICID('/home/mytest/test1') 

4. 启动 trigger monitor deamon 监控服务
unix:  # runmqtrm -m YourQueueManager -q INITQ.Q1 &
win:  c:\> runmqtrm -m YourQueueManager -q INITQ.Q1 
注意,这个deamon 进程 不可以停掉。

5, 你的 /home/mytest/test1 程序,就是你正常写的 mq 的读取程序,没有特别的变化,只是前面提到的如何驻留逻辑需要考虑清楚,这个和 mq 没有一点关系了。

6, 可以放入测试消息到 Q1 中,测试了。

7, 开心笑吧, 呵呵

下面的 url, 你可以找到一些别人以前遇到的问题的解决办法。可以参考
http://www-900.ibm.com/cn/suppor... id=0&category=5

 

 

11#-

针对您的回答,我还有几个不明白的
1、既然通道建立连接是在建立到队列管理器的连接时,那么这个通道连接断开是不是也是执行qMgr.disconnect()时断开?

2、您说过通过MQ的自身触发机制的trigger 例子中
定义Q1 trigger 中要用到的 Process
DEFINEPROCESS (P1) +
APPLTYPE(UNIX) +
APPLICID('/home/mytest/test1') 
我通过资料查到APPLTYPE含义是要启动的应用程序的类型,这个应用程序的类型是不是指我的接收程序是在什么操作系统下开发的,如果我是在WINDOWSNT下开发的,那这值是不是应该设置为“WINDOWSNT”?APPLICID 指要启动的应用程序的名称,如果这样的话是不是队列管理器在哪台机器上运行,我的接收程序就得放到哪台的机器上?如果是这样的话,我们的客户端有很多,一个客户端对应一个队列,每个客户端的接收程序可能不同,有的是用java开发的,有的是用c#开发的,是不是每个队列都要建一个进程,而且每个客户端的接收应用程序都要放置到队列管理器所在的服务器上?不知道我这个理解对不?
我看到网上有这么定义进程的DEFINE PROCESS(P_NT)+
APPLTYPE(WINDOWSNT)+
APPLICID('runmqchl -cSDR_NT -m QM_NT')
这里的APPLICID参数是一个运行通道的命令,这是什么作用呢?

3、我似乎已经想到了为什么我的接收程序长时间运行会出现通道关闭的问题了,也许只是其中一个因素,因为我的接收程序中有这么一块代码:
while((msg = fetchMsg(queue)) != null) {
doc =(Document) msg.readObject();
......
}
在读取完消息对象后我还调用了一些类中的方法,也就是还有其他一些操作,如果这些操作出现异常以后就不会执行关闭队列和队列管理器对象的操作
queue.close();
qMgr.disconnect();
所以我觉得将这两个操作放到finally中,应该会关闭一些已经打开但是没用的连接,不知道我这个分析有道理没?

 

12#-

无关紧要内容略…

 

13#--11#

回复:
问题1.  你自己都想到答案了, 看看资料,然后,在程序中打印点自己的 debug 信息,测试一下,答案不就更明确了嘛

问题2. 
APPLTYPE和 APPLICID ,前面我给你的 link 里面有详细的解释,这个最好看资料说明的更清楚;

首先对于客户端的程序,不支持 trigger 的方式; 其次, 从 mq 队列中读取(get )消息,一定是需要应用程序自己主动的来读取(即使是 trigger ,也只是trig 启动你的应用程序,然后你的应用程序主动来读取消息),这是面向消息中间件设计的原则;

如果可以不考虑成本,所有的地方用 mq server, 大家都欢喜, 呵呵

问题3. 
这种情况是通常我们应用最容易犯的,也容易遗漏的错误,好好检查一下应用的健壮性, 同时, 从产品的角度, 也可以把前面 channel 中的参数项设定上,保证产品管理上的健壮。


此转载内容另附word文档供下载 标题即为资源名称

原创粉丝点击