WebsphereMQ编程篇第二十三章编程设计.docx
《WebsphereMQ编程篇第二十三章编程设计.docx》由会员分享,可在线阅读,更多相关《WebsphereMQ编程篇第二十三章编程设计.docx(17页珍藏版)》请在冰豆网上搜索。
WebsphereMQ编程篇第二十三章编程设计
第二十三章编程设计
消息设计
消息大小(MessageSize)
WebSphereMQ中与消息大小的相关参数如下:
对象
属性
缺省值
范围
说明
队列管理器
MAXMSGL
4M
32K-100M
队列
(Local/Model)
MAXMSGL
4M
0-QM.MAXMSGL
0表示用QM.MAXMSGL
通道
MAXMSGL
4M
0-QM.MAXMSGL
0表示用QM.MAXMSGL
队列管理器的MAXMSGL表示队列管理器中可以容纳的最大消息长度。
此参数只对AIX、CompaqOpenVMS、HP-UX、Linux、OS/2Warp、OS/400、Solaris和Windows有效。
缺省值为4,194,403,范围在32,768到104,857,600之间。
队列管理器的MAXMSGL的值应该始终大于等于队列的MAXMSGL值,如果这个值调小了,应该检查队列管理器中所有的Local或Model队列的MAXMSGL属性,包括系统队列SYSTEM.DEFAULT.LOCAL.QUEUE的定义。
队列的MAXMSGL仅对Local和ModelQueue有效,表示队列中可以容纳的最大消息长度。
●对于AIX、CompaqOpenVMS、HP-UX、Linux、OS/2Warp、OS/400、Solaris和Windows平台,队列的MAXMSGL值从0到队列管理器的MAXMSGL(上限为100MB)。
●如果z/OS平台,大小为0-104,857,600Bytes。
●对于其它平台,大小为0-4,194,304Bytes。
对于传输队列,消息的最大长度也包括消息传输头的大小。
如果这个值调小了,已经在队列中的超长消息不受影响。
应用程序可以用这个值来决定需要申请的内存,所以调大该值有可能会影响应用程序的正常运行。
通道的MAXMSGL,表示Channel可以一次传送的最大消息长度。
●对于AIX、iSeries、HP-UX、Linux、Solaris和Windows,以及MQSeriesV5.1forOS/2Warp,andVSE/ESA,通道的MAXMSGL的大小应该大于等于0,小于等于队列管理器的MAXMSGL。
●对于z/OS平台,大小为0-104,857,600Bytes。
●对于其它平台,大小为0-4,194,304Bytes。
通道在建立的时候会有一个握手过程,双方会交换各自通道定义上的MAXMSGL,最后协商出通道使用的最大消息长度,一般会取双方定义中较小的那一个。
如果通道的两端支持消息切分,则一旦实际传送的消息长度比通道的可以传送的最大消息长度(MAXMSGL)更大也不要紧,MQ会自动将其切分后送出,在远端自动拼接起来。
只要小于队列管理器的MAXMSGL和队列的MAXMSGL,使本地队列能够放得下这条消息即可。
如果通道的两端都不支持消息切分,且消息实际传送的消息长度比通道的MAXMSGL大,则出错。
消息持久性(Persistence)
WebSphereMQ中与消息持久性的相关参数如下:
对象
属性
缺省值
可取值
说明
消息
(MQMD)
DefPersistence
Q_DEF
●MQPER_PERSISTENCE_AS_Q_DEF
●MQPER_PERSISTENT
●MQPER_NOT_PERSISTENT
队列
DEFPSIST
NO
●YES
●NO
z/OS中用Y或N
通道
NPMSPEED
FAST
●NORMAL
●FAST
消息头MQMD属性DefPersistence可以有三种取值,但实际上只有两种有效值,即持久或非持久。
对于MQPER_PERSISTENCE_AS_Q_DEF,消息在MQPUT/MQPUT1时会设置队列的缺省属性。
持久的消息可以在队列管理器重启时恢复,非持久的消息则不可以。
持久的消息写入或读出队列的同时会在Log中记录,所以性能上比非持久消息差不少。
队列属性DEFPSIST表示放入其中的消息缺省是持久的(Persistence)还是非持久的(Non-Persistence)。
持久消息不可以放入临时动态队列(TemporaryDynamicQueue)。
因为考虑到临时动态队列可以在MQOPEN时创建,在MQCLOSE时删除,在队列管理器重启后也会自动清除。
这与持久消息不会丢失的性质不相容,所以WebSphereMQ禁止持久性消息放入临时动态队列,在程序运行时报错:
MQRC_PERSISTENT_NOT_ALLOWED。
通道的属性NPMSPEED(NonpersistentMessageSpeed)如果设置成NORMAL并不会有什么特殊的功能。
如果设置成FAST,则非持久消息的传送可以不参加交易,交易中的非持久消息一旦到达传输队列会立即送出,这可以使非持久消息的传送速度大大加快。
缺点是消息可能丢失,可能泄露到交易之外。
丢失指消息传送失败或通道关闭,消息不会被回滚到传输队列中。
泄露指发送方交易回滚,但消息是非持久消息,立即送出,已达对方,不可回滚。
NPMSPEED只对AIX、HP-UX、iSeries、Solaris、Windows等平台上的Sender、Cluster-Sender、Server、Receiver、Cluster-Receiver、Requester通道有效。
消息优先级(Priority)
WebSphereMQ中与消息优先级相关的参数如下:
对象
属性
缺省值
可取值
说明
队列管理器
MAXPRTY
9
9
这个值无法修改
队列
(Local/Model)
DEFPRTY
0
0–9
队列
(Local/Model)
MSGDLVSQ
PRIORITY
●PRIORITY
●FIFO
消息
(MQMD)
Priority
Q.DEFPRTY
队列管理器的MAXPRTY属性表示队列管理器中允许的最大消息优先级,缺省为9,消息可以取0–QM.MAXPRTY中的任何一个值。
本地队列或模型队列的DEFPRTY属性表示队列上消息的缺省优先级,在MQPUT/MQPUT1时如果MQMD.Priority=MQPRI_PRIORITY_AS_Q_DEF(-1),则消息取值为队列的DEFPRTY属性。
本地队列或模型队列的MSGDLVSQ属性可以设置消息传递次序(MessageDeliverySequence)的方式,即MQGET时消息选取的次序。
●PRIORITY消息按优先级从高到低排序,同优先级按先进先出排序
●FIFO按先进先出排序
一旦指明FIFO,则设置MQMD.Priority不再生效,新放入队列的消息优先级为队列的缺省优先级属性DEFPRTY。
事实上,PRIORITY和FIFO的本质是相同的,都是以优先级为第一索引,以时间为第二索引。
只是FIFO方式中优先级都一样,等于DEFPRTY。
如果队列的MSGDLVSQ属性从PRIORITY变成FIFO,那么队列中可能存有一些Priority低于DEFPRTY的消息,新到的消息优先级为DEFPRTY(不管是否指定MQMD.Priority),所以新到的消息可能比原先的低优先级消息更早地被处理。
反过来,如果MSGDLVSQ从FIFO变成PRIORITY,队列中原有的消息优先级应该是DEFPRTY。
消息超时(Expiry)
在消息头MQMD中的Expiry域为消息的超时时间,即消息的有效生命周期,单位是1/10秒。
这个值表示消息从放入队列开始,应该在多长的时间内被取出处理。
缺省情况下,MQMD.Expiry=MQEI_UNLIMITED(-1),表示消息永不超时。
Expiry的记时与超时原理如下:
1.从消息开始放入队列开始计时。
不允许MQMD.Expiry=0,否则返回MQRC_EXPIRY_ERROR。
这时MQMD.PutDate和MQMD.PutTime会记录下MQPUT/MQPUT1的时间。
2.消息在路由的过程中可能会被队列管理器多次放入传输队列并取出传送,所有这些在路上耽搁的时间都会计算在内。
事实上,这些内部的MQPUT/MQGET动作会使Expiry时间衰减。
每次消息进入传输队列,队列管理器会在消息头上加上一个MQXQH,其中也有Expiry,它会标记消息在这段传输路程上的耗时,在脱去MQXQH时会反应到MQMD.Expiry。
3.消息最终到达目标本地队列,在那里进入排队。
在开放平台(Windows,UNIX)的队列管理器中并没有超时监控程序始终监视队列中的每一条消息是否超时。
事实上,WebSphereMQ直到消息被MQGET扫描到才会被发现超时。
4.队列可能是FIFO方式,也可能是Priority方式,并且MQGET还可能有匹配条件。
这需要队列管理器对队列中的消息从头开始扫描,扫描到的消息都会根据消息创建时间(MQMD.PutDate,MQMD.PutTime),消息超时(MQMD.Expiry)及当时时间计算出消息时否超时。
如果超时,则被删除。
如果消息带有超时报告标志(MQRO_EXPIRATION_*),则同时发送超时报告。
由于开放平台上WebSphereMQ消息超时的发现依赖于MQGET对队列的扫描,所以消息的有效生命周期是可以保证的,但超时点可能不准确。
换句话说,如果消息躺在队列中一百年,尽管早已超时,只要始终未被MQGET扫描到,就一直不会收到超时报告。
对FIFO队列而言,不含匹配条件的MQGET会从第一条消息一直扫描到第一个未超时的消息。
对Priority队列而言,同样的MQGET会扫描所有更高优先级的消息一直扫描到等高优先级中的第一个未超时的消息。
扫描过的部分可能有多条消息被发现超时。
z/OS上的WebSphereMQ有独立的消息自动监控机制。
队列管理器有ExpiryInterval属性表示每隔多长时间队列管理器自动扫描所有的消息,检查是否有消息超时,并做出相应的处理。
ExpiryInterval单位是秒,取值1-99,999,999。
也可以取值MQEXPI_OFF,关闭此功能。
发送设计
消息标识
WebSphereMQ中每一条消息都有三个标识:
MsgId,CorrelId和GroupId。
其中,MsgId是消息自身的标识,CorrelId是消息的相关标识,GroupId是消息所有组的标识。
这三个标识都是MQMD中的域,长度为24字节。
在MQPUT或MQPUT1时,应用程序可以设置MQMD.MsgId来设定消息标识。
通常来说,应用系统中不应该出现两条标识相同的消息。
所以由应用程序自动设定消息标识的办法并不稳妥,这需要程序自己来保证消息标识的唯一性。
如果在发送消息的时候,MQMD.MsgId为空,或者设置了发送选项MQPMO.Options=MQPMO_NEW_MSG_ID,则队列管理器会自动生成一个唯一标识来标记这条消息,该标识由消息标识头“AMQ”、队列管理器名简称、时戳等信息构成,理论上队列管理器不会生成两条MsgId完全相同的消息。
这样就自动地保证了消息标识的唯一性,不致在匹配消息时引起混乱。
然而,自动生成的消息标识可能含有非打印字符,在表达上稍显困难。
在使用分发列表(DistributionList)时,一条消息可能在传送的过程中自动复制成多条消息,为了使WebSphereMQ自动为这些消息生成不同的MsgId,必须设置MQMD.MsgId为空,或使用MQPMO_NEW_MSG_ID选项。
CorrelId表示相关的消息标识,比如应答消息用CorrelId指明相关的请求消息,报告(Report)消息指明相关的原消息等等。
在WebSphereMQ的应用中,可以有多条消息与某一条原消息相关。
也就是说,可以有多条MsgId不同的消息,它们的CorrelId是相同的。
应用程序可以自行设定MQMD.CorrelId来设定相关标识。
类似于MsgId,如果设置发送选项MQPMO.Options=MQPMO_NEW_CORREL_ID,则消息会自动生成CorrelId,规则同MsgId。
GroupId表示消息所在的组标识,相同的组标识表示这些消息属于同一个组,在匹配时可以整组匹配。
详见,“分组与分段”章节。
消息类型
MQMD的MsgType域标志着消息类型。
它可以表示消息是请求(Request)还是相应的应答(Reply),是应用消息(Datagram)还是相应的报告消息(Report)。
在发送时设定消息类型,主要是使接收程序在收到该消息后便于区别,从而做出正确的反应。
通常编程时,MQMD.MsgType可以取值:
●MQMT_REQUEST
●MQMT_REPLY
●MQMT_DATAGRAM
●MQMT_REPORT
消息格式
消息内容有一定的组织格式,比如说是一个结构。
结构中的域有些是整数类型的,也有些是字串类型的。
由于WebSphereMQ所处的操作系统平台不同,从而会引起整数型数据在高低字节编码上的不同,也会引起字串型数据所使用的字符集不同。
MQMD中三个域Format、Encoding和CodedCharSetId分别可以指明消息的格式、编码、字符集。
Format可以取值:
●MQFMT_NONE
●MQFMT_ADMIN
●MQFMT_CHANNEL_COMPLETED
●MQFMT_CICS
●MQFMT_COMMAND_1
●MQFMT_COMMAND_2
●MQFMT_DEAD_LETTER_HEADER
●MQFMT_DIST_HEADER
●MQFMT_EVENT
●MQFMT_IMS
●MQFMT_IMS_VAR_STRING
●MQFMT_MD_EXTENSION
●MQFMT_PCF
●MQFMT_REF_MSG_HEADER
●MQFMT_RF_HEADER
●MQFMT_RF_HEADER_2
●MQFMT_STRING
●MQFMT_TRIGGER
●MQFMT_WORK_INFO_HEADER
●MQFMT_XMIT_Q_HEADER
消息分为消息头(MQMD)和消息体两部分。
MQMD.Format表示消息体的数据结构,Format可以认为是消息体的数据结构名。
不过也有例外,MQFMT_STRING表示消息体是字串,并非一个结构。
如果不知道消息体的结构,则可以用MQFMT_NONE来表示。
消息的发送和接收平台可以不一样,消息可以从一种格式转向另一种格式。
详见“格式转换”章节。
应答队列
应用程序通过WebSphereMQ进行请求/应答式通信的时候,请求方发送消息后通常希望应答消息自动放入指定的队列,该队列就是应答队列。
在请求消息的MQMD结构中的ReplyToQMgr和ReplyToQ域指明了应答队列的位置。
应答方收到这样的请求消息后,经过消息处理,产生相应的应答消息,会“心领神会”地将应答消息放入应答队列中。
当然,设置应答队列,对于应答方应用程序而言,只有“指导意义”而无“强制意义”。
应答队列的设置,可以使相同的应答程序与不同的请求程序形成多个请求/应答消息环路。
请求方可以指定各自的应答队列,互不干扰。
另一种做法是多个请求方共用一个应答队列,发送请求消息的时候保证消息的MsgID不重复,取应答消息的时候匹配各自消息的CorrelID。
这要求应答方要请求消息的MsgID拷贝到应答消息的CorrelID,以表示是针对哪一条请求消息的应答。
用户替换
消息在从应用程序进入队列的时候,消息的发送者会记入MQMD的UserIdentifier域,缺省情况下这就是运行应用程序的用户。
如果在MQOPEN时指定MQOD.AlternateUserId,并选择MQOO_ALTERNATE_USER_AUTHORITY选项,则WebSphereMQ会用AlternateUserId来打开该队列,实现用户替换。
以后的MQPUT会以该用户作为MQMD.UserIdentifier。
类似地,MQPUT1中含MQOPEN,可以指定MQOD.AlternateUserId,并选择MQPMO_ALTERNATE_USER_AUTHORITY选项,则效果与MQOPEN相同。
在用户替换的过程中,如果MQOD.AlternateSecurityId为空,则使用MQOD.AlternateUserId来进行权限检查。
否则,可以使用MQOD.AlternateSecurityId。
比如,在Windows中不同的机器可以有相同的用户名(UserId),但某个用户名在域中有唯一的安全标识(SecurityId),可以用这样的SID放入MQOD.AlternateSecurityId进行权限认证。
读取设计
等待读取(Wait&NoWait)
MQGET可以等待读取消息,即先设定消息的匹配条件,然后在队列上等待消息的到来。
当然也可以不设条件,等待第一条消息的到来。
这时,设置MQGMO.Options=MQGMO_WAIT,且设置MQGMO.WaitInterval为等待时间,单位毫秒。
在设定的时间内有匹配的消息到来,则MQGET读到消息,返回MQRC_NONE。
否则,MQGET超时,返回MQRC_NO_MSG_AVAILABLE。
如果MQGMO.WaitInterval=MQWI_UNLIMITED(-1),即超时设定为无穷大,则MQGET会永远等待在队列上,直到匹配的消息到来。
MQGET也可以非等待方式读取。
设置MQGMO.Options=MQGMO_NO_WAIT,MQGET时如果队列中没有匹配的消息,则立即返回。
信号中断
对于WebSphereMQforz/OS、CompaqNonStopKernel、Win95/98环境。
可以用信号将消息读取中断。
在MQGET时设置MQGMO_SET_SIGNAL选项,并在MQGMO结构的Signal1和Signal2中设定信号值,则在特定的时候会有信号中断出现。
应用程序可以通过截取信号中断的方法,实现并发同时读多个队列。
对于z/OS,信号值设在Signal1中,队列管理器自动送出的ECB(EventControlBlock)中含有信号完成码:
●MQEC_MSG_ARRIVED
●MQEC_WAIT_INTERVAL_EXPIRED
●MQEC_WAIT_CANCELED
●MQEC_Q_MGR_QUIESCING
●MQEC_CONNECTION_QUIESCING
对于CompaqNonStopKernel,信号值设在Signal1中,中断消息会发送到进程的$RECEIVE队列。
对于Windows,信号值设在Signal2中,中断消息以Windows系统消息的方式送给该进程。
截断消息(TruncatedMessage)
MQGET的时候如果队列中的消息长度大于准备接收的Buffer长度,则可能发生消息截断,即消息被取出,但只有前一部分放入Buffer中,后一部分数据丢失。
有时应用上的确需要这种截断功能,比如只需要读取消息的前100个字节。
这时,设置MQGMO.Options=MQGMO_ACCEPT_TRUNCATED_MSG。
在MQGET时,如果消息长度大于BufferLength,则DataLength表示消息的实际长度,消息被取出,但只读到前一部分,返回MQRC_TRUNCATED_MSG_ACCEPTED。
有时应用上需要读取完整的消息,避免截断情况的发生。
这时,MQGMO.Options上不要设定MQGMO_ACCEPT_TRUNCATED_MSG选项。
在MQGET时,如果消息长度大于BufferLength,则DataLength表示消息的实际长度,消息仍然保留在队列中,返回MQRC_TRUNCATED_MSG_FAILED。
通常在这种情况下,应用程序应该按DataLength分配实际需要的内存,再进行一次MQGET将消息读出。
在MQGET时如果发生数据转换,则消息的长度可能会有所变化。
在MQGET返回后,如果MQGMO.ReturnedLength不为MQRL_UNDEFINED(-1),则表示返回的字节数,通常情况下与DataLength相同。
如果为MQRL_UNDEFINED,则以DataLength为准。
浏览消息(Browse)
浏览消息本质上是读消息的一种特殊方式,只是消息队列中并没有被自动删除。
在打开队列时用MQOO_BROWSE指明浏览方式打开,在MQGET时MQGMO.Options=MQGMO_BROWSE_NEXT,可以按匹配条件依次读取消息内容。
在队列打开时会创建一个游标,其工件原理类似于数据库游标,它会记住当前浏览的位置,以后调用MQGET(MQGMO_BROWSE_FIRST)可以游标定于匹配消息集合的第一条上,MQGET(MQGMO_BROWSE_NEXT)可以依次浏览。
MQGET(MQGMO_BROWSE_MSG_UNDER_CURSOR)可以再次浏览游标指向的消息,游标不动。
MQGMO_BROWSE_*可以和MQGMO_LOCK一起使用,将这条消息锁住,此消息对其它并发应用变为不可见。
接着可以用非浏览方式的MQGET(MQGMO_MSG_UNDER_CURSOR)将该消息读走。
MQOPEN(MQOO_BROWSE)
MQGET(MQGMO_BROWSE_FIRST+MQGMO_LOCK)
while(notfound)
{
MQGET(MQGMO_BROWSE_NEXT+MQGMO_LOCK)
if(found)break;
}
MQGET(MQGMO_MSG_UNDER_CURSOR)
格式转换(Convert)
WebSphereMQ中的数据格式可以根据需要自动地转换成目标字符集和编码方式。
所谓字符集(CodedCharSetId)就是指消息的字符所属的文字集,如单字节英语码,双字节汉语码等等。
编码方式(Encoding)指的是整数的高低字节安排、浮点数的精度和幂的安排等等,通常应该符合IEEE标准。
WebSphereMQ中的格式转换可以出现在三个地方:
1.在MQGET的时候,用MQGMO_CONVERT选项,这时MCA在接收消息的时候,会根据消息中的CodedCharSetId和Encoding域与应用程序的环境进行比较,如果需要则自动进行转换。
2.在发送方的通道设置属性CONVERT=YES,这时MCA在发送消息的时候,会根据消息中的CodedCharSetId和Encoding域与对应队列管理器的环境进行比较,如果需要则自动进行转换。
3.在发送方或接收方设置数据转换用户出口,在情况1或2中需要数据转换时,WebSphereMQ会根据消息类型(Type域)来调用同名