MQ系統管理編程概述

在廣大用戶使用MQ的過程中,大家比較常用而且比較熟悉的是有關MQI的編程,即利用MQ應用程序接口進行消息的發送和接收,以C語言爲例,最爲常 見的是使用MQCONN, MQOPEN, MQPUT/MQGET, MQCLOSE, MQDISC等函數進行隊列的讀寫操作;而大家對MQ的系統管理編程可能比較陌生,希望得到更多這方面的知識和技巧,本文將向大家介紹該方面的編程內容和 技巧。

所謂的MQ系統管理編程是指使用MQ提供的編程接口,編制特定的應用程序,來進行對MQ各種對象的監控和管理,如:查詢通道的狀態、隊 列的深度等運行時狀態,查詢隊列管理器的屬性,隊列的屬性或通道的屬性等靜態參數;以及對MQ進行各種操作,如:啓動/停止通道,創建/刪除各種對象,修 改各種對象的屬性等。

實際上,MQ爲我們提供了多種系統管理的手段,其中包括:

  • 利用MQ的圖形管理界面進行操作、監控和管理:MQ資源管理器爲用戶提供了良好的管理界面,從而方便用戶對中間件系統的配置和管理工作,通過MQ資源管理器,用戶可以定義、修改、刪除中間件的各種資源、對象,察看各種對象的屬性,監控各種對象的運行狀態;
  • 利用MQSC命令,通過命令行的方式對MQ進行操作、監控和管理,尤其值得一提的是MQSC命令在各個平臺上都是相同的,在一個有若干平臺共存的系統中大大地方便和簡化了系統管理的工作。
  • 通過特定的系統管理工具進行監控和管理,MQ不僅自身提供了GUI和文本化監控工具來監測和顯示對象的狀態,並且能夠利用各類系 統管理軟件對整個網絡運行狀況實施監控;如Tivoli Manager for MQ就是一個功能強大的綜合管理工具:它爲控制消息和應用程序框架提供了一個集中管理平臺。它捕獲和顯示MQ的事件,並能發現新的隊列管理器以及相應的對 象,這些對象可以通過中央控制檯進行定義和配置。除此之外,許多第三方廠家也提供有類似的管理工具。

除了以上這三種管理手段之外,MQ還提供了系統管理的編程接口,通過該接口用戶可以編寫應用程序從而進行自動化的實時監控及管理。MQ的系統管理接口有兩種,即Programmable Command Format(可編程命令格式,簡稱PCF)和MQ Administration Interface(MQ管理接口,簡稱MQAI)。下面,我們分別介紹這兩種編程接口的原理和使用。

第一部分:WebSphere MQ 可編程命令格式(PCF)

WebSphere MQ 可編程命令格式(PCF)命令使得管理任務能編寫到應用程序中,在程序中可以創建隊列、進程等對象的定義以及更改對象的屬性等。

PCF定義了命令和回覆消息,應用程序通過這些命令和回覆消息實現和隊列管理器之間的信息交換,PCF 命令和MQSC 命令具有相同的命令集,所有通過MQSC命令能夠實現的功能,通過PCF都可以實現,因此,通過WebSphere MQ的應用程序可以實現對MQ對象的管理包括:隊列管理器,進程定義,隊列和通道等。PCF命令可以被髮送到本地隊列管理器的命令隊列,也可以被髮送到某 個遠程隊列管理器的命令隊列,因此,應用程序可以通過一個本地隊列管理器集中管理網絡中的任何本地和遠程管理器。

MQ的遠程管理機制底層就是通過PCF這種方式的,在互相聯接的系統中的任意一個節點都可以進行對其他所有節點的配置和管理,這種情形 的典型應用就是通過一臺Windows操作系統的機器來管理全網的MQ節點。由於MQ在Windows XP/NT/2000平臺上提供了圖形界面的管理工具,我們可以把一個節點設成管理機,利用管理機可以監控和配置網絡中的任一節點,監測和顯示整個網絡中 任何一個節點上的服務器及其各種對象的狀態和運行情況,從而實現對中間件的集中管理和監控。

每一個隊列管理器有一個名爲SYSTEM.ADMIN.COMMAND.QUEUE的管理隊列,應用程序可以按照PCF命令消息格式封 裝的要求,組成PCF消息,並將該PCF命令消息發送到管理隊列中,同時,每一個隊列管理器也有一個命令服務器(Command Server),它爲管理隊列中的消息提供服務,在我們使用MQ的控制命令strmqcsv啓動命令服務器之後,它將監控管理隊列,一旦該隊列中有PCF消息到達,它將讀取該消息,並解釋執行。因此在網絡中的任何隊列管理器都可以處理PCF消息,通過使用指定的回覆隊列,回覆消息可以被返回給應用程序,應 用程序可以獲知PCF命令執行成功與否。回覆隊列由命令消息的消息描述符(MQMD)中的ReplyToQ和ReplyToQMgr兩個字段來指定。

PCF命令和回覆消息是使用MQ相應的編程接口進行發送和接收的,以C語言爲例,我們只需要使用MQPUT將PCF命令消息發送到相應 的管理隊列,使用MQGET將PCF回覆消息從相應的回覆隊列中取出即可。這裏的關鍵就是如何封裝PCF消息。每個MQ指令及其相關參數都是一條單獨的命 令消息,每個命令消息由PCF頭和若干個參數結構塊組成,每個參數結構塊提供了命令的參數。回覆消息的結構與命令消息相同,但是回覆消息的個數根據不同的 情況可能會有多個,例如:如果我們查詢某個隊列管理器下所有本地隊列的屬性,假設本地隊列有10個,那麼我們將得到10個回覆消息,PCF頭中的Control字段MQCFC_NOT_LAST和MQCFC_LAST將用於區分是否爲最後一個回覆消息。

PCF編程接口支持C,Visual Basic, COBOL, RPG, PL/1和Java等,其中在我們最常用的編程語言中,C和Visual Basic編程在PCF的封裝上相對Java將會略微複雜一些。

PCF頭用來描述希望執行的PCF命令以及緊接PCF頭之後的參數結構塊的個數,例如:如:MQCMD_INQUIRE_Q 的PCF命令用來查詢隊列的屬性,MQCMD_INQUIRE_CHANNEL_STATUS用來查詢通道的運行狀態;當希望查詢通道的狀態時,我們要指 出欲查詢通道的名稱以及類型,這樣將會有2個參數結構塊。

在PCF頭之後緊接着是各個參數結構塊,參數結構塊有PCF String, PCF Integer, PCF Integer List,PCF String List四種類型。通常情況下,PCF String用來描述PCF命令要操作的對象的名稱,例如,若我們要查詢名爲TEST通道的狀態時,我們要將PCF String的通道名稱設置爲TEST;PCF Integer用來描述PCF命令要操作的對象的類型, 例如,若我們要查詢名爲TEST通道的當前運行狀態時,我們要將PCF Integer中的通道實例類型名稱設置爲MQOT_CURRENT_CHANNEL; PCF Integer List用來描述PCF命令要操作的對象的某些屬性,若我們要查詢名爲TEST通道的當前運行狀態和當前的通道消息序列號時,我們要在PCF List中設置MQIACH_CHANNEL_STATUS和MQCACH_CURRENT_LUWID兩個屬性。下面,我們給出一個使用C語言來查詢通 道狀態的程序示例片斷,見附件1:GetChlStatus.c。

以上我們介紹了PCF的原理和基於C語言的編程實例,下面,我們再給大家介紹一下PCF對Java編程接口的支持。缺省情況下,在您所 購買的MQ的產品介質中,是不提供PCF for Java開發環境的,但是,IBM通過SupportPac的形式免費向所有用戶提供該支持。相信使用和熟悉MQ的用戶,一定知道MQ及其產品家族的SupportPac,各種SupportPac可以供用戶從MQ家族產品的網站上免費下載獲得,如果登陸http://www.ibm.com /software/mqseries/txppacs,大家就能夠獲得與之相關的信息,並且可以免費下載其中感興趣的內容,這裏爲我們廣大用戶提供了一 些非常有用的工具。例如:在這些支持軟件包中,你可以得到有關產品配置方法的信息,有關產品系統管理的信息,有關產品性能測試結果的信息,有關產品規劃時 的一些建議和方法論,有關產品使用中的一些使用工具,有關產品編程方面的一些樣例程序等。

其中,編號爲ms0b、名稱爲"MQSeries Java Classes for PCF"的SupportPac,就提供了PCF的Java編程接口支持。它其中包括了用於PCF Java編程的開發包:com.ibm.mq.pcf.jar、有關PCF Java開發的說明文檔以及若干示例程序。

在PCF Java接口中,爲我們提供了CMQC, CMQCFC, CMQXC, MQCFH, MQCFIL, MQCFIN, MQCFSL, MQCFST, PCFAgent, PCFMessage, PCFMessageAgent, PCFHeader, PCFParameter, PCFException等若干類,每個類有其相應的屬性和方法,通過對這些類的屬性設置和方法調用,我們就可以通過Java編寫PCF程序,實現對MQ的系統管理編程。

下面,我們仍以查詢通道的當前運行狀態爲例,來介紹PCF Java的基本方法和步驟。首先進行變量的初始化和PCF命令消息的封裝:

PCFAgent agent;
int [] attrs =
{CMQCFC.MQIACH_CHANNEL_STATUS};
PCFParameter [] parameters =
{
//設置通道名稱爲TEST,它屬於我們前面講述的PCF String
new MQCFST (CMQCFC.MQCACH_CHANNEL_NAME,"TEST");,
//設置查詢的是當前運行的通道,它屬於我們前面講述的PCF Integer
new MQCFIN (CMQCFC.MQIACH_CHANNEL_INSTANCE_TYPE, CMQC.MQOT_CURRENT_CHANNEL),
//設置查詢的是當前運行狀態屬性,它屬於我們前面講述的PCF Integer List
new MQCFIL (CMQCFC.MQIACH_CHANNEL_INSTANCE_ATTRS, attrs)
};
MQMessage [] responses;

接下來,創建PCFAgent類,並進行相應的函數調用,發送PCF命令消息:

if (args.length == 1)
{
System.out.print ("Connecting to local queue manager " +
args [0] + "... ");
//直接連接本地隊列管理器
agent = new PCFAgent (args [0]);
}
else
{
System.out.print ("Connecting to queue manager at " +
args[0] + ":" + args[1] + "over channel" + args[2] + "... ");
//以MQ 客戶端的方式連接隊列管理器
agent = new PCFAgent(args[0], Integer.parseInt(args[1]), args[2]);
}
System.out.println ("Connected.");
agent.setCharacterSet(1381);
// 通過PCFAgent發送PCF命令
System.out.print ("Sending PCF request... ");
//發送的PCF命令爲查詢通道狀態
responses = agent.send (CMQCFC.MQCMD_INQUIRE_CHANNEL_STATUS, parameters);
System.out.println ("Received reply.");

這裏,PCFAgent是很重要的一個類,它維護了與隊列管理器之間的一個連接,並且提供相應的方法來發送PCF命令消息並獲得回覆消 息。並且,從版本V2.0開始,PCF Classes for Java又爲我們提供了名稱爲PCFMessageAgent的另外一個類,它是PCFAgent的子類,它使用在版本V2.0推出的另外一個新的 類:PCFMessage來封裝PCF命令消息和回覆消息,從而避免了用戶對MQ的消息,PCF頭以及參數結構塊的直接操作,因此,比PCFAgent更 加簡單易用,而更加受到廣大用戶的青睞。

爲了讓您體驗一下PCFMessageAgent的方便之處,下面我們給出使用PCFMessageAgent來查詢通道當前運行狀態的程序示例:

首先進行變量的初始化:

PCFMessageAgent agent;
PCFMessage request;
PCFMessage [] responses;

接下來,創建PCFMessageAgent類,並進行相應的函數調用,發送PCF命令消息,:

if (args.length == 1)
{
System.out.print ("Connecting to local queue manager" +
args [0] + "... ");
//直接連接本地隊列管理器
agent = new PCFMessageAgent (args [0]);
}
else
{
System.out.print ("Connecting to queue manager at " +
args[0] + ":" + args[1] + "over channel" + args[2] + "... ");
//以MQ 客戶端的方式連接隊列管理器
agent = new PCFMessageAgent (args [0],
Integer.parseInt (args [1]), args [2]); }
System.out.println ("Connected.");
// 創建PCF命令消息
request = new PCFMessage (CMQCFC.MQCMD_INQUIRE_CHANNEL_STATUS);
request.addParameter (CMQCFC.MQCACH_CHANNEL_NAME,"TEST");
request.addParameter (CMQCFC.MQIACH_CHANNEL_INSTANCE_TYPE,
CMQC.MQOT_CURRENT_CHANNEL);
request.addParameter (CMQCFC.MQIACH_CHANNEL_INSTANCE_ATTRS,
new int [] {CMQCFC.MQIACH_CHANNEL_STATUS});
// 通過PCFMessageAgent發送PCF命令消息
System.out.print ("Sending PCF request... ");
responses = agent.send (request);
System.out.println ("Received reply.");

第二部分:WebSphere MQ 管理接口(MQAI)

除了PCF的系統管理編程接口之外,WebSphere MQ還提供另外一種系統管理編程接口,即:MQ管理接口(MQ Administration Interface,簡稱爲MQAI)。MQAI是MQ 提供的一種簡化的、實現發送和接收PCF命令消息和回覆消息的接口,MQAI通過使用數據包(Data Bags)來處理對象的屬性,這樣比直接使用PCF更簡單。

在MQ for Windows, AIX, iSeries, Linux, HP-UX, and Solaris and WebSphere MQ for OS/2 Warp版本,都支持MQAI。目前,MQAI支持的編程語言不如傳統的PCF那麼豐富,只有C和Visual Basic兩種。

MQAI的底層工作機制同PCF一樣,也是通過發送PCF命令消息到MQ命令服務器隊列,從而被命令服務器解釋執行,並等待回覆消息來管理WebSphere MQ,如圖所示:


圖:MQAI的原理圖
圖:MQAI的原理圖

MQAI通過傳遞參數到數據包的方法,從而提供了更簡單的訪問PCF消息的編程接口。這樣只要一條語句就可以實現一個結構,編程人員不需要處理數組和分配空間,也不需要了解PCF的消息結構。

每個數據包含有若干個數據項(Data Items),這些數據項在數據包內被有序排列,它們的排列順序被稱爲插入序號(Insertion Order)。每個數據項由一個選擇器(Selector)和一個數值(Data Value)組成,其中,選擇器是數據項的標識,而數值可以是整數、字符串或者另外一個數據包的句柄。

數據包有用戶包(User Bag), 管理包(Administration Bag),命令包(Command Bag)和系統包(System Bag)等不同的類型,其中,管理包用於封裝發給MQ命令服務器的管理命令,管理包自動包含若干選項;系統包將在命令服務器生成回覆消息時自動生成。數據 項則有用戶數據項和系統數據項兩種,其中,用戶數據項包含了諸如你要管理的對象屬性的數據;系統數據項用於控制生成的消息,諸如消息頭的生成。

使用MQAI的一般步驟如下:

1. 確定你要採取的系統管理行爲,明確你要執行的命令;

2. 確定選擇器和相關的參數;

3. 利用mqCreateBag函數調用生成數據包,並且使用mqAddInteger, mqAddString, mqAddInquiry函數調用生成選擇器對應的數據項;

4. 檢查MQ命令服務器是否處於運行狀態;

5. 使用mqExecute函數調用向MQ命令服務器發送消息,並獲得相應的回覆消息。

下面,我們以C語言爲例,給出使用MQAI的程序樣例,這裏,我們將使用MQAI來查詢隊列的當前深度。

首先,進行變量初始化,並建立與隊列管理器的連接:

MQHCONN hConn; /* MQ 連接句柄 */
MQCHAR qmName [MQ_Q_MGR_NAME_LENGTH+1 ]=""; /*缺省隊列管理器*/
MQLONG reason; /*原因碼*/
MQLONG connReason; /*MQCONN reason code */
MQLONG compCode; /*完成碼*/
MQHBAG adminBag =MQHB_UNUSABLE_HBAG; /*mqExecute使用得管理數據包*/
MQHBAG responseBag =MQHB_UNUSABLE_HBAG; /*mqExecute的響應數據包*/
MQHBAG qAttrsBag; /*含有隊列屬性的數據包*/
MQHBAG errorBag;
MQLONG mqExecuteCC; /*mqExecute完成碼*/
MQLONG mqExecuteRC; /*mqExecute原因碼*/
MQLONG qNameLength;
MQLONG qDepth;
MQLONG i;
MQLONG numberOfBags;
MQCHAR qName [MQ_Q_NAME_LENGTH+1 ];
//連接隊列管理器
if (argc >1)
strncpy(qmName,argv [1 ],(size_t)MQ_Q_MGR_NAME_LENGTH);
MQCONN(qmName,&hConn,&compCode,&connReason);
if (compCode ==MQCC_FAILED)
{
CheckCallResult("Queue Manager connection",compCode,connReason
exit((int)connReason);
}

第二步,創建用於封裝命令消息的管理數據包

mqCreateBag(MQCBO_ADMIN_BAG,&adminBag,&compCode,&reason);
CheckCallResult("Create admin bag",compCode,reason);
//創建用於封裝回復消息的數據包
mqCreateBag(MQCBO_ADMIN_BAG,&responseBag,&compCode,&reason);
CheckCallResult("Create response bag",compCode,reason);
//向管理數據包中添加要查詢的隊列的名稱,這裏,我們將查詢所有本地隊列的深度
mqAddString(adminBag,MQCA_Q_NAME,MQBL_NULL_TERMINATED,"*",
&compCode,&reason);
CheckCallResult("Add q name",compCode,reason);
//向管理數據包中添加要查詢的隊列的類型,這裏,我們將查詢所有本地
隊列的深度
mqAddInteger(adminBag,MQIA_Q_TYPE,MQQT_LOCAL,&compCode,&reason);
CheckCallResult("Add q type",compCode,reason);
//向管理數據包中添加要查詢的是隊列的當前深度這一屬性mqAddInquiry(adminBag,
MQIA_CURRENT_Q_DEPTH,&compCode,&reason);
CheckCallResult("Add inquiry",compCode,reason);
//調用mqExecute,通過該調用將產生PCF消息,然後會將此消息發送到管理命令
隊列:SYSTEM.ADMIN.CONMMAND.QUEUE,並且得到含有回覆消息的應答數據包
mqExecute(hConn, /* MQ 連接句柄 */
MQCMD_INQUIRE_Q, /*要執行的命令 */
MQHB_NONE,
adminBag, /*管理數據包句柄 */
responseBag, /*響應數據報句柄*/
MQHO_NONE, /*將消息發送到SYSTEM.ADMIN.COMMAND.QUEUE*/
MQHO_NONE, /*爲響應產生一個動態隊列*/
&compCode, /* mqExecute的完成碼 */
&reason); /* mqExecute call 的原因碼*/

第三步,處理響應數據包:

if (compCode ==MQCC_OK )/*Successful mqExecute */
{
//計算響應數據包中系統數據包的個數,每個隊列的屬性分別在不同的數據包中
mqCountItems(responseBag,MQHA_BAG_HANDLE,
&numberOfBags,&compCode, &reason);
CheckCallResult("Count number of bag handles",compCode,reason);
for (i=0;i<numberOfBags;i++)
{
//處理每個系統數據包
mqInquireBag(responseBag,MQHA_BAG_HANDLE,
i,&qAttrsBag,&compCode,
&reason);
CheckCallResult("Get the result bag handle",compCode,reason);
//獲得隊列名稱
mqInquireString(qAttrsBag,MQCA_Q_NAME,0,MQ_Q_NAME_LENGTH,qName,
&qNameLength,NULL,&compCode,&reason);
CheckCallResult("Get queue name",compCode,reason);
//獲得隊列當前深度
mqInquireInteger(qAttrsBag,
MQIA_CURRENT_Q_DEPTH,MQIND_NONE,&qDepth,
&compCode,&reason);
CheckCallResult("Get depth",compCode,reason);
mqTrim(MQ_Q_NAME_LENGTH,qName,qName,
&compCode,&reason)
printf("%4d %-48s \n",qDepth,qName);
}
}
else
{
printf("Call to get queue attributes failed:Completion Code =%d :
Reason =%d \n",compCode,reason);
//錯誤處理,獲得錯誤處理包
if (reason ==MQRCCF_COMMAND_FAILED)
{
mqInquireBag(responseBag,MQHA_BAG_HANDLE,
0,&errorBag,&compCode,
&reason);
CheckCallResult("Get the result bag handle",compCode,reason);
//錯誤處理,獲得錯誤處理包
mqInquireInteger(errorBag,MQIASY_COMP_CODE,
MQIND_NONE,&mqExecuteCC, &compCode,&reason );
CheckCallResult("Get the completion code
from the result bag", compCode,reason);
//錯誤處理,從錯誤處理包中獲得原因碼
mqInquireInteger(errorBag,MQIASY_REASON,
MQIND_NONE,&mqExecuteRC, &compCode,&reason);
CheckCallResult("Get the reason code from the
result bag", compCode,reason);
printf("Error returned by the command
server:Completion Code =%d :
Reason =%d \n",mqExecuteCC,mqExecuteRC);

第四步,刪除所使用的數據包:

if (adminBag !=MQHB_UNUSABLE_HBAG)
{
mqDeleteBag(&adminBag,&compCode,&reason);
CheckCallResult("Delete the admin bag",compCode,reason);
}
if (responseBag !=MQHB_UNUSABLE_HBAG)
{
mqDeleteBag(&responseBag,&compCode,&reason);
CheckCallResult("Delete the response bag",compCode,reason);
}

最後,需要斷開與隊列管理器的連接:

if (connReason !=MQRC_ALREADY_CONNECTED)
{
MQDISC(&hConn,&compCode,&reason);
CheckCallResult("Disconnect from queue manager",compCode,reason);
}

以上向大家介紹了MQ的系統管理編程接口PCF和MQAI,由於大家在使用MQ的過程中,經常希望通過此類接口編寫應用程序,對MQ的有關資源進行自動化的監控和管理,從而增強系統的可維護性和健壯性,希望本文在這方面對大家有所幫助。




從上面的程序代碼可以看出,使用PCFMessageAgent和PCFMessage這兩個類,大大簡化了對PCF命令消息的封裝工作,只需要給出相應的參數即可。我們在附件2中給出了PCFMessageChannelStatus.java程序源代碼。

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