IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  WebSphere  >

WebSphere MQ出口程序编写和配置

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

娄丽军, 软件部售前工程师, IBM公司

2003 年 6 月 01 日

在使用WebSphere MQ时,大家可能会进行出口程序的编写,灵活地使用通道出口可以更大地发挥MQ的功能,本文对此专题进行了详细说明和论述,相信您会有所启发!

1 出口程序的概念

使用WebSphere MQ(以下简称MQ)做分布式数据传输时,我们需要在两个队列管理器之间或者在MQ的客户端与服务器之间建立通道,MQ的通道分为三大类:消息通道,MQI通道,Cluster通道。其中消息通道用于队列管理器之间的数据传输,它主要有Sender,Receiver, Server,Requester四种类型;MQI通道用于Client与Server端的通讯,它可以认为是一种API通道,使得客户端能够通过API编程访问和使用服务器端的消息队列资源;Cluster通道是位于同一个队列管理器群集内部的若干队列管理器之间相互通讯用的。

为了使用户在利用MQ进行消息传输时增加对消息的客户化处理以及对通道的控制,MQ提供通道出口(Channel Exit)的解决方案。出口程序在定义通道时指明,并由MCA(消息通道代理)程序调用,目的在于增强通道的处理以及提供安全服务和消息处理服务。

利用出口机制,客户可将自己的加密、压缩等计算处理模块平滑地嵌入到MQ的消息传输处理流程中,从而使这些计算处理的算法部分与进行队列读写的应用程序相分离,这样根据灵活性和扩展性的需要,可以经常改变出口的计算处理算法,而不需修改应用程序。

MQ提供的出口程序类型主要有:安全出口(Security Exit), 发送/接收出口(Send/Receive Exit), 消息出口(Message Exit), 消息重试出口(Message Retry Exit)等。

一般而言,出口程序是成对使用的,譬如在发送端使用了加密出口程序,则在接收端必须使用解密出口程序。这些出口程序的作用分别在于:

  • 安全出口:主要用于两个MQSeries系统之间通道启动时的双方的身份认证;
  • 发送和接收出口:可以用来进行数据的加密/解密以及数据的压缩/解压缩;
  • 消息出口:可以用来在消息级实现用户的特定功能,如数据转换,加密/解密等。

例如,利用通道出口程序将数字签名加在通道上的每一条消息上,该数字签名被接收端通道代理程序接收,进行相关验证,在消息从发送端到接收端传输的过程中,绝不会被更改。WebSphere MQ为用户提供了一些非常实用的工具:SupportPac软件包,用户可以从产品的Internet站点上下载使用,其中的MS0C就是一个如何使用MQ的安全出口实现PKI认证的例子。





回页首


2 出口程序的工作机制

2.1 安全出口的工作机制

安全出口主要用于通道两端的MCA互相连接时,进行对方的安全验证。它在MCA初始化和终止时被调用。在通道启动时,MCA程序从启动会话转为同步处理,然后转向数据交换,其中包括安全出口程序,这个相互认证过程必须成功完成之后才能进行消息传输。

2.1.1 安全出口认证过程:

安全检查是一个循环过程,(Caller MCA是指发起连接者,可以是Sender、Server或Requester通道;被发起者称为Responder MCA,可以是任何种类的通道。),如图1所示:


在两个安全出口程序中间需要建立起安全连接,一个在发送端MCA,另一个在接收端MCA。在上面的安全循环中,本地队列管理器要由远端队列管理器鉴别自己,而作为回应,远端队列管理器也要由本地队列管理器鉴别自己。这是一个双向的过程。

发送端MCA启动通道后,由接收端MCA创建安全令牌以初始化安全信息,然后发送安全令牌给本地队列管理器上的MCA,发送端MCA接收到安全令牌后,利用认证机制将该令牌作为入口创建另外的令牌,并将它发送回接收端MCA。接收端MCA检查第二次收到的令牌,完成相互鉴定。如果以上操作成功,接收者队列管理器就确认与发送者队列管理器的通信,即发送端可以向接收端传输消息。安全出口成对使用,任何一方都可以终止连接。

2.1.2 安全出口应用场合:

安全出口虽然名为"安全"出口,但它并不仅仅只用于安全目的,也并非所有的与安全有关的功能都必须在这里实现,用户完全可以灵活地利用它实现自己需要的功能,例如,在可以免费下载的MQ的SupportPac中,针对MQ for Windows 95/98平台,曾经提供了一个安全出口的例子程序,用于将通道的Connection Name从对方的IP地址改为对方的队列管理器名。

安全出口在发送端和接收端均可以返回以下几种结果之一:

  • 安全认证无误结束;
  • 禁止并关闭通道;
  • 发送安全消息给远端相应的安全通道;
  • 发送安全消息,并且要求应答(OS/390平台上使用CICS时,无此支持)

注意:

安全出口通常成对使用。如果定义了通道,必须确认通道两端具有一致的安全出口程序名。

2.2 消息出口的工作机制

消息出口作用于通道的任何一端,对每一个消息调用一次,即:在消息离开传输队列之后被放入目的队列之前。消息出口在以下这些情况下被调用:

  • MCA初始化和终止时;
  • 发送MCA执行MQGET调用从传输队列中取出消息后(见图2);
    图 2
    图 2
  • 接收MCA执行MQPUT调用向本地队列放入消息前(见图3)。
    图 3
    图 3

2.2.1 消息出口应用场合:

  • 消息数据加密:消息出口一般用于消息传输前的加密和传输后的解密,其中的加/解密算法可以由用户自行定义。
  • 消息数据转换
  • 日志
  • 其它一些应用

消息出口可以返回以下几种情况之一:

  • 消息可以被取走(GET出口),消息已经被出口程序正确处理(返回MQXCC_OK);
  • 被放入队列中(PUT出口),消息已经被出口程序正确处理(返回MQXCC_OK);
  • 不能正确处理消息,消息被MCA放入死信队列。
  • 关闭通道。
  • 错误返回码,造成MCA异常终止。

注意:

每次完整的消息传输仅仅调用消息出口一次,即使是消息被分割成几部分也是如此。

2.3 发送/接收出口的工作机制

发送/接收出口对每一个消息段(message segment)调用一次,即如果您对消息进行了分段处理,则每一段都将被发送/接收出口处理。消息出口在以下这些情况下被调用:

  • MCA初始化和终止时;
  • 在发送端数据被发送之前,在接收端数据被接收之前。

2.3.1 发送/接收出口应用场合:

  • 与消息出口类似,发送/接收出口也可以用于数据加密/解密,压缩/解压缩,不同的是,它对每一个消息段调用一次,而不仅限于完整的消息,因此,用于消息内部数据结构不是很重要的场合,即把每一段当作一个"blob"来处理。

2.4 出口程序调用顺序

如上图所示,各种出口程序的执行顺序是:

1)安全出口在通道两端数据初始化时被调用,它们必须被成功执行以便于使通道启动阶段正常结束,从而允许消息的传输。在通道启动之前利用安全出口,与对方交换一些信息来实现安全认证的工作。

2)消息出口被发送端MCA调用,然后发送出口被调用,从而处理被传输的消息。

3) 当接收端收到消息时,调用接收出口,然后调用消息出口。

它们之间的其他区别在于:

对于不同类型的通道,可以设置的通道出口类型不同。

Security Exit必须成对使用,对应的agent buffer只包含有关的安全数据,它不能操作或Access消息体的内容,所以它无法用来对消息进行加密,压缩处理。

Send/Receive Exit通常情况下成对使用,它对应的agent buffer包含具体的传输数据,因此能够操作消息体的内容;对于send exit,agent buffer的前8个字节为保留字,专为MCA所用。

MsgExit可以不必成对使用,它对应的agent buffer包含MQ的传输头以及具体的消息体,它也能够操作消息体的内容。

下表总结了各种类型的通道支持的出口类型以及对应的agent buffer的内容:

ExitChannel TypeBuffer data MQXCP+
SdrSvrRcvrRequrCLNTCONNSVRCONN
SecurityYesYesYesYesYesYesSecurity msg
MessageYesYesYesYes  MQXQH+msg
SendYesYesYesYesYesYes8bytes+data
ReceiveYesYesYesYesYesYes8bytes+data

其中,MQXCP代表通道出口程序数据结构;

MQXQH代表传输队列头





回页首


3 出口程序的编写

各类出口程序在各种平台上的编写结构基本都是一样的。下面以NT/2000平台上的消息出口程序编写为例说明之。

3.1 出口程序中缓冲区的大小:

  • 对消息出口,缓冲区应允许最大消息和MQXQH结构大小之和;
  • 对安全出口,缓冲区可以分配一个4000字节;
  • 对发送/接收出口,缓冲区不能大于:
    TCP:
        AS/400:16KB
     Others:32KB
    UDP:
        32KB
    NetBIOS: 
        DOS:4 KB  
        Others:64 KB  
    LU 6.2: 
        OS/2:64 KB  
        Others:32 KB
            
                

3.2 出口程序是动态链接库(NT/2000平台)

在Windows NT/2000平台中,由MMC(微软管理控制器)控制服务管理接口进行管理,以确保该动态链接库在需要时被调用。

定义出口程序的路径的方法有:

  • 用户可以在定义通道(DEFINE CHANNEL)时指定带有全路径的文件名;
  • 通过MQ Services图形界面更改队列管理的ExitPath属性指定出口路径名。

下面实例中,以MQ安装时设定的目录c:\mqm\exits作为出口程序的存放目录,并在定义通道的消息出口属性时定义全路径。

3.3 消息出口程序编写说明:

本文附件将提供一个消息出口程序,其作用是将消息数据进行简单的加密处理,由于它是对原数据的每一个字节取反,因此它同时也可以位于通道的另一端对消息进行解密处理。该程序名为msgexit.c。

以msgexit.c为例,消息出口程序的基本框架大致如下:

#include <cmqc.h>
#include <cmqxc.h>
 
void MQStart() {;}          /*dummy entry point - for consistency only */
void MQENTRY MsgExit     ( PMQCXP  pChannelExitParms,
                           PMQCD   pChannelDefinition,
                           PMQLONG pDataLength,
                           PMQLONG pAgentBufferLength,
                           PMQVOID pAgentBuffer,
                           PMQLONG pExitBufferLength,
                           PMQPTR  pExitBufferAddr)
{
/* 变量定义 */
PMQCXP        pParms;   
PMQCD         pChDef;
pParms   = (PMQCXP)pChannelExitParms;
pChDef   = (PMQCD)pChannelDefinition;
... 在此加入用户代码
}
    

为了能够访问pChannelExitParms和pChannelDefinition两个指针,用户必须在出口程序中加入开始的这几行内容,通过pParms 和 pChDef进行访问。样板程序中没有访问此两个指针,故没有定义这几行。

使用Visual C++编译程序,需要作以下几步操作:

1)将MQMVX.LIB作为源文件加入项目文件中;

2)将C/C++项目设置中的"Use Run-Time Library"从"Multithreaded"改变到"Multithreaded using DLL",

3)不要改变C/C++项目Link设置中的"Entry-Point Symbol"。

3.4 DEF样板文件(msgexit.def):

  LIBRARY MSGEXIT
 PROTMODE
 DESCRIPTION 'Provides Channel Message exits'
 CODE SHARED    LOADONCALL
 DATA MULTIPLE
 HEAPSIZE   4096
 STACKSIZE  8192
 EXPORTS MsgExit
 
    





回页首


4 出口程序配置步骤

4.1 创建出口程序的动态链接库

以消息出口程序msgexit.c为例创建相应的链接库。

  • 编译连接:cl /MT /LD msgexit.c msgexit.def
  • 将生成的msgexit.dll文件放在exit程序专用的目录下,c:\mqm\exits

4.2 环境设置

1) 创建队列管理器,设置通道

假设我们在两台Windows2000系统上,各建一个队列管理器QMA和QMB,并且在QMA上建立发送通道A.TO.B,在QMB上建立接收通道A.TO.B,以及所需的本地队列,传输队列和远程队列。消息将从QMA发送到QMB。

2)增加通道消息出口定义

在接收端定义接收通道中的消息出口:

ALTER CHANNEL(A.TO.B) CHLTYPE(RCVR) +
MSGEXIT('c:\mqm\exits\MSGEXIT(MsgExit)')
    

在发送端定义发送通道中的消息出口:

ALTER CHANNELL(A.TO.B) CHLTYPE(SDR) +
MSGEXIT('c:\mqm\exits\MSGEXIT(MsgExit)')
    

4.3 出口程序的验证

4.3.1 安全出口程序的验证方法:

  • 首先,在没有调用安全出口的情况下,使用PING CHANNEL命令检查通道是否可用。
  • 在创建了库文件、配置完毕后,可以在有出口的情况下测试系统。首先还是应使用PING CHANNEL命令检查通道是否可用。
  • 如果在编译出口程序时选择DEBUG编译模式,则可以对PING通道的初始化、中断的过程进行跟踪。
  • 然后,定义远程队列,向其中放入消息,观察通道的状态,并在目的端从其接收队列中取出消息。
  • 以上步骤通过后,并且消息传输无误,则出口程序验证成功。

4.3.2 消息出口程序的验证方法:

  • 在MQA端上,用 amqsput <远程队列名> 向远程队列中放入若干文本消息。
  • 在MQB端上,用 amqsget <本地队列名> 从接收队列取出这些消息。
  • 比较放入的文本与取出的文本,双方一致说明消息出口程序处理正确。
  • 查看c:\msgexit.log文件,以了解msgexit的运行日志。

附件:消息出口源程序msgexit.c

/* standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
/*#ifdef AIX*/
#include <langinfo.h>
/*#endif*/
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
/* MQSeries headers */
#include <cmqc.h>
#include <cmqxc.h>
char timebuf[130];
char Channel_Logfile[129]="msgexit.log";
FILE * OST = 0;
 
/* define output file and statement prototype */
#define DBGPRINTF(x) { \
         OST=fopen(Channel_Logfile,"a+"); \
         if(OST) fprintf x; \
         fclose(OST); \
         }
void encrypt(long *, char *); /* prototype for file encrypting function */
/* dummy function used as entry point to exit, only needed for AIX boxes */
void MQENTRY MQStart(void) {;}
void MQENTRY MsgExit( PMQCXP     pChannelExitParams,
      PMQCD      pChannelDefinition,
      PMQLONG    pDataLength,
      PMQLONG    pAgentBufferLength,
      PMQBYTE    AgentBuffer,
      PMQLONG    pExitBufferLength,
      PMQPTR     pExitBufferAddr)
   {
 struct timeb t;
 char millisec[25];
 
 /*** Get time string to use in all messages   ***/
 time_t clock = time( (time_t*) NULL);
 struct tm *tmptr = localtime(&clock);
 strcpy(timebuf, asctime(tmptr));
 ftime( &t );
 memset( millisec, 0, sizeof( millisec ) );
 sprintf( millisec, " Time:%ld.%d", t.time, t.millitm );
 strcat( timebuf, millisec );
 /*** Check call type - initialization, message, or exit ***/
 /*                                                        */
 switch( pChannelExitParams-> ExitReason )
  {
  case MQXR_INIT:
     DBGPRINTF((OST,"\nChannel message exit called at MCA initialization %s",timebuf));
     break;
  case MQXR_MSG:
     /*** Call subroutine to encrypt the message ***/
     /*                                                    */
     encrypt(pDataLength, (char *)AgentBuffer);
     DBGPRINTF((OST,"\nMessage Transfering, length = %li %s \n",*pDataLength, timebuf));
     break;
  case MQXR_TERM:
     DBGPRINTF((OST, "\nMessage channel exit called at MCA termination %s\n",timebuf));
     break;
  default:
     DBGPRINTF((OST,"\nMessage channel exit called with invalid reason code: %d  %s", 
     pChannelExitParams-> ExitReason, timebuf));
     break;
  } /* switch */
 
 /*** Set exit response = OK in all circumstances ***/
 pChannelExitParams -> ExitResponse = MQXCC_OK;
 return;
      
}/* end exit */
/*------------------------------------------------------------------
   encrypt() - encrypt a MQSeries message with user-defined algorithm.
-------------------------------------------------------------------*/
void encrypt(long *lngth, char *bf)
{
long i,j;
unsigned char ch;
long temp = sizeof(MQXQH);
j = * lngth;
/*DBGPRINTF((OST,"\nMQXQH length :  %d\n",temp));*/
for(i = temp; i < j; i++)
 {
  ch = bf[i];
  ch = 0 - ch;
  bf[i] = ch;
/*DBGPRINTF((OST,"\nMessage number:  %d",i));*/
 }
} /* end writer */
    



关于作者

娄丽军, IBM公司  软件部售前工程师,1998年加入IBM公司软件部,四年来一直从事IBM通讯及业务整合中间件(WebSphere Business Integration家族)产品的技术支持工作,是软件部从事该领域技术支持时间最长的工程师之一,拥有WebSphere Business Integration相关的产品经验,这些产品包括WebSphere MQ家族的所有产品:MQSeries, MQ Integrator, MQ Workflow以及CrossWorlds等,并具有很多大型项目的支持经验,曾参与国家税务总局,人民银行清算系统、华夏银行电子联行系统、中国联通计费系统、海关与国家税务总局互连系统,以及公安部、铁道部、中信实业银行、电信等重要客户的有关项目的技术支持。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款