MQ異常 關閉原因 = 2009[個人收藏 他人求助及回答]

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文檔供下載 標題即爲資源名稱

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章