IBM MQ消息头详细介绍

细看 MQ 消息头(MQMD)的功能算法

  MQMD 是每一个消息都带有的消息头信息,它由若干字段组成,这些字段都是 MQ 设计人员根据总结的应用需求而设置的。应用程序构建消息时应该对这些字段填入恰当的值,对于没有填入的字段,MQ会用默认值填充。开发应用程序时,充分理解并利用这些字段是十分必要的,这里逐一为你们进行介绍,并针对每一个字段指明它在实际编程中通常会用来实现什么样的功能:数据库

  StrucId:消息头结构名,固定为"MQMD"四个字符。根据这个字段,咱们就可以在应用程序数据包中识别出MQMD的位置。编程

  Version :MQMD 版本号。服务器

  Report:消息的报告选项,默认值为 MQRO_NONE。发送方程序经过设置此字段值以指定在消息传递出现意外、消息超时、消息到达、消息递出事件时是否须要报告消息和报告消息要包含什么内容。对于须要消息报告的程序,须要对这个字段与下面介绍的 ReplyToQ 和 ReplyToQMgr 字段一块儿进行设置,以对这些消息事件做出反应。数据结构

  Expiry:超时字段,单位是 0.1 秒,默认值是 MQEI_UNLIMITED,表示永不过时。消息放到目标队列里之后,若是超过这个指定时间尚未被程序读走,MQ 系统就会丢弃这个消息。若是这个消息设置了 Report 字段要求超时报告,系统会按照Report字段指定的方式返回一个超时报告。应用中一般要进行必要的消息超时机制设计,好比实现SOA框架下为保持交易一致性而普遍使用的 Compensation 机制,就能够用超时处理实现。并发

  Feedback:反馈字段,此字段与 Report 字段一块儿使用以指示报告的性质。框架

  Encoding:消息中数值数据(binary integer、packed-decimal integers、floating-point numbers等)的编码方式,默认值是 MQENC_NATIVE,因平台而异,此值不适用于 MQMD 结构自己的数字数据。实际应用中,为减小复杂性,要尽可能少使用二进制的数值数据,这时就没必要考虑此字段。异步

  CodedCharSetId:指定消息使用的字符集编码的 CCSID,默认值是 MQCCSI_Q_MGR,随平台不一样而不一样。MQ 在须要转码时根据这个字段的值来识别消息内容的编码方式,在主机上通常使用包含 GBK 字符集的 CCSID1388,对应的 UNIX 和 WINDOWS 系统下的 CCSID 是 1386。分布式

  Format:给出描述消息体所符合的数据格式名称,格式名能够本身定义,默认值是 MQFMT_NONE。应用程序可使用这个字段来指定发送消息的格式名,接收方根据这个名字对消息体作出不一样解释。ide

  Priority:消息的优先级,最低优先级是0,默认值是MQPRI_PRIORITY_AS_Q_DEF。

  Persistence:消息持久性值,默认值为 MQPER_PERSISTENCE_AS_Q_DEF。若是消息是持久的,全部操做会记入MQ LOG;若是消息不是持久的则不记 LOG,MQ 系统中断或重启意味着还没被处理的消息将丢失。应用程序设计时要对是否使用持久性消息进行深刻的考虑,虽然持久性消息比较可靠,但它的性能比非持久消息有很大的落后,若是可使用应用逻辑来保证数据一致性,尽可能少使用持久性消息。

  MsgId :消息标识,它用来区分消息,由 MQ 自动生成,任意两个消息的 MsgId 都不一样。程序执行 MQPUT 后能从 MsgId 字段获得发出消息的 ID。MsgId 在某个 QMGR 里是惟一的,但理论上两个 QMGR 可能产生相同的 MsgId,虽然这种状况实际上极少会出现。编程上要注意,不要把两个 QMGR 产生的 MsgId 进行比较。

  CorrelId:消息相关标识,应用程序可以使用它来将一个消息与另外一个消息相关,或将一个消息与应用程序正在执行的其它工做相关,默认值全为空。在一般的作法中,发送请求消息的程序记录下请求消息的 MsgId,服务程序读到请求消息,拿出它的 MsgId 放到回复信息的 CorrelId 字段中,发送程序在 MQGET 获得回复消息前,先把记录的 MsgId 填到消息头的 CorrelId 中,这样它就能 GET 到那条特定的回复消息。CorrelId 也能够用来设计更复杂的消息传递/识别机制。

  BackoutCount:记载消息被回滚的次数。具体介绍参见有害的消息处理。

  ReplyToQ:这是回复消息队列的名称。本字段和下面的 ReplyToQMgr 一块儿,构成了消息返回目的地信息。一般消息请求程序在发送请求时,就填好这些字段,消息处理程序只简单地根据要求进行回复,经过这种方式实现动态的消息回送机制。

  ReplyToQMgr:这是回复消息队列所在队列管理器的名称,其默认值全为空,表示返回消息时到本地队列管理器中去找 ReplyToQ。

  UserIdentifier:它属于 MQMD 的 identity context 字段,是发起消息的应用程序的用户标识。 其默认值为空。

  AccountingToken:它属于 MQMD 的 identity context 字段,容许应用程序计算由消息引发的工做量的信息。其默认值为空。

  ApplIdentityData:它属于 MQMD 的 identity context 字段,是由应用程序定义的信息,可用来提供有关消息或其发起方的信息。其默认值为空。应用的请求和服务端能够进行协商,规定这个字段的一些专门用途,经过这个字段,来实现必定的自动化处理。

  PutApplType:它属于 MQMD 的 origin context 字段,是放入消息的应用程序类型,标志在一个消息传递串中最近的对消息进行处理的程序的信息。例如 CICS、IMS、BROKER 等。其默认值为 MQAT_NO_CONTEXT。经过本字段和下面的PutApplName字段,消息接收程序能够识别某条消息是谁发送过来的,并根据状况进行特殊的处理。

  PutApplName:它属于 MQMD 的 origin context 字段,是放入消息的应用程序的名称。其默认值为空。

  GroupId:消息组标识,MQ for z/OS 不支持消息分组。

  MsgSeqNumber:组中逻辑消息的顺序号。MQ for z/OS 不支持消息分组。

  Offset:数据的偏移量,MQ for z/OS 不支持消息分组技术。

  MsgFlags:主要是与消息分组相关的一些状态信息。

  OriginalLength:分段消息的原始长度。

  MQMD 的这些字段为咱们应用程序的开发提供了很好的设施,例如当应用程序请求方须要一种方法肯定哪些返回信息是针对哪条请求时,典型的作法有两种,一种是为每一个请求动态建立一个临时队列,把队列名填入 ReplyToQ 字段,响应程序根据 ReplyToQ 里的值肯定每条消息返回到哪一个队列里去;另外一种方法是响应方把原始请求的 MsgId 字段拷贝到它所发回的消息的 CorrelId 字段里去,发送方用 MsgId 搜索返回信息。

  又如 MQ 发送消息的消息头里包含了所谓的消息上下文(message context)信息,这些字段描述了消息发送者的一些状况,消息上下文又包括两部分:身份鉴别上下文(Identity context)和发送者上下文(Origin context)。身份鉴别上下文(Identity context)描述了消息最初是由谁产生的,包括 MQMD 的 UserIdentifier、AccountingToken和ApplIdentityData 字段;发送者上下文(Origin context)描述了把消息放到队列上的应用程序的状况,包括 MQMD 的 PutApplType, PutApplName, PutDate, PutTime, ApplOriginData 字段。当应用程序把一个消息进行转发时,能够选择是从新生成这些上下文仍是从原消息里继承上下文。一般的作法是最初的消息发送程序由系统根据用户信息生成全部消息上下文,对消息作修改或者转发的应用,只新生成发送者上下文(Origin context),而身份鉴别上下文(identity context) 最好传递从原始消息获得的上下文信息,这样在消息处理中的任意环节,都可以了解到最初发动者的用户信息。

  根据实际业务需求,灵活地运用这些字段,能够方便地实现复杂的系统功能。

  消息类型与传输控制

  企业中经过 MQ 传输的消息有多种不一样的类型,不一样类型的消息有不一样的处理方式。一般消息能够分为如下几类:

  数据报(datagram)消息

  一种典型的异步消息传递形式,其中应用程序发送消息但不须要响应。这种消息类型是结构最简单、效率最高的类型,应用设计时要尽可能可能地使用这种模式,经过 MQ 提供的可靠的消息传输确保消息可以获得处理,对于要求较高的环境,能够设置错误报告机制。

  报告消息(report message)

  给出另外一个消息相关信息的消息。报告消息可以代表消息已发送、已到达目标、已到期或因为某种缘由没法处理。部分报告信息能够经过适当的设置,由系统在须要的状况下自动发送,另外一些能够由应用程序根据特定的状况发送。获得报告的程序,一般须要设置发出消息的 Report、ReplyToQ、ReplyToQMgr 字段来设置但愿的报告类型、返回队列名和返回队列管理器。在应用设计中,把业务需求和报告消息的功能结合起来,可以解决不少种实际需求。

  同步的请求/应答消息(request/reply message)

  经过 MQ 实现同步的请求/应答机制:一个程序发送请求消息,经过 MQ 传送给消息处理方,而后到特定队列上去守听应答消息。消息处理程序从 MQ 获取输入后,进行特定的处理,而后把应答消息写入返回队列。请求程序获得应答后继续完成后续处理。这是与前两种方式相比效率较低的一种模式,它是在异步通讯的基础上实现的同步应用需求。这时请求/应答程序间要经过上述的动态队列、MsgId、CorrelId 等手段完成消息的识别。这时还须要进行数据完整性的一些考虑,见下面的分析。

  应用的交易一致性控制

  MQ 事务支持特性

  和传统的交易处理系统同样,MQ 把应用程序分红若干工做单元(UOW),每一个工做单元内部对数据作的更新一般是逻辑相关的,必须同时成功或回滚以保持数据完整性。为此,MQ 提供了专门的 API:MQBEGIN、MQCMIT 和 MQBACK,分别表示工做单元的开始、提交和回滚。

  MQ 不但能够提供队列数据操做上的一致性,并且经过全局事务协调器(RRS)的协调下,MQ 能够实现全局的数据一致性,例如一个程序不但处理了 MQ 消息,并且同时处理了 DB2 数据库数据、CICS 的 VSAM 文件,这些操做能够经过统一的一个确认或回滚获得一致。要注意在 CICS 中的 MQ 程序,工做单元的控制将使用 CICS的EXEC CICS SYNCPOINT 或 SYNCPOINT ROLLBACK,而不是 MQI 的命令。

  程序经过每一个 MQGET 和 MQPUT 操做数据时,能够选择此次操做是放在 UOW 以内仍是UOW 以外,经过对读、写时的选项参数进行设置。若是某个操做放在 UOW 以外,它的操做将不能回滚。

  对于在工做单元以内的 MQPUT 操做,因为交易隔离机制,在 COMMIT 以前,其余程序是看不到这条消息的,这点在编程中须要注意。

  有害消息的处理

  因为 MQ 消息处理有事务特性,若是队列里某条消息数据结构存在问题,程序处理它时会发生失败,这条消息会被自动回滚到队列中,下次当它再被读出时,又可能发生失败,这种状况若是没有被意识到,可能会引起严重的错误循环。为防止这种状况的发生,MQ 在消息头 MQMD 里设计了 BackoutCount 字段,若是某个消息是在同步点控制之下读取的,而且因为某种缘由消息被回滚,消息描述符中的 BackoutCount 字段的值将被加1,良好的程序设计须要判断该数值,若是它大于某个阈值,则须要使用其它手段来处理该消息,好比再也不对数据进行分析直接放到某个问题队列里去。在处理该消息的应用中,能够将其与设定的阈值作比较,这时,阈值会被写死在程序中,为了提升其灵活性,还可使用队列的 BOTHRESH 和 BOQNAME 属性。这样,在例外处理中,利用 MQINQ 查询获得阈值 BOTHRESH 的大小,若是超出,能够将消息转发到 BOQNAME 指定的队列中,继而对该队列进行相应的处理。这种方法大大加强了应用程序的灵活性。

  异步通讯下保持数据一致性的设计

  在使用 MQ 进行同步通信的程序设计时,会碰到原来可能会作单一 UOW 的应用,在MQ 的异步应用设计下要划分红若干个 UOW:发送程序 PUT 到队列里是一个 UOW,接收程序 GET 又是一个 UOW,这就涉及到如何在多 UOW 下保证数据总体的一致性的问题。这种需求,通常能够经过灵活地使用 MQ 提供的消息生命周期功能和应用的冲正逻辑进行配合。在典型的状况下:

  假设请求系统向服务系统发出请求,调用服务系统上的某个数据改动交易,在请求系统作MQPUT 时,首先要设置请求消息的生命周期 T1,而且在消息到期时将消息丢弃,若是在消息发走以前消息过时,它就会在进入通道以前,被 MQ 系统丢弃;若是消息到达目的地以后,在被对方应用程序取走以前消息过时,它也将被 MQ 系统丢弃。请求系统发送请求后,到应答队列里去取结果,若是时间 T1 已过,数据还没有返回,就给用户显示"交易失败,超时"信息。

  在服务系统上,应答程序处理完请求,修改数据库后,返回结果消息,结果消息也设置生命周期T2(T2<T1),与请求消息不一样的是,咱们设置在消息到期时发送一个超时报告,将结果消息转发到另一个特定的队列中,在这个队列上用触发器挂一个冲正程序,这个程序对于全部超时而没有被请求系统的程序读走的消息,发动冲正,也就是把前台没有收到返回的交易都恢复回去。

  在这种设计下,若是请求系统发送后,交易数据没有被应答系统读走就超时了,请求系统可以正确显示"交易失败,超时",这条请求信息会被系统超时抛弃;若是应答系统已经读取并完成了数据更改,因为意外缘由应答消息没有被请求方读到,请求方显示"交易失败,超时"后将不会再读这条应答消息,它必将过时,过时后被系统复制到冲正队列,触发自动冲正,恢复了数据,整个的效果也是交易失败,数据没有改动。经过这样的设计,能够看到数据一致性可以很好地保持。

  应用的交易一致性控制

  MQ 事务支持特性

  和传统的交易处理系统同样,MQ 把应用程序分红若干工做单元(UOW),每一个工做单元内部对数据作的更新一般是逻辑相关的,必须同时成功或回滚以保持数据完整性。为此,MQ 提供了专门的 API:MQBEGIN、MQCMIT 和 MQBACK,分别表示工做单元的开始、提交和回滚。

  MQ 不但能够提供队列数据操做上的一致性,并且经过全局事务协调器(RRS)的协调下,MQ 能够实现全局的数据一致性,例如一个程序不但处理了 MQ 消息,并且同时处理了 DB2 数据库数据、CICS 的 VSAM 文件,这些操做能够经过统一的一个确认或回滚获得一致。要注意在 CICS 中的 MQ 程序,工做单元的控制将使用 CICS的EXEC CICS SYNCPOINT 或 SYNCPOINT ROLLBACK,而不是 MQI 的命令。

  程序经过每一个 MQGET 和 MQPUT 操做数据时,能够选择此次操做是放在 UOW 以内仍是UOW 以外,经过对读、写时的选项参数进行设置。若是某个操做放在 UOW 以外,它的操做将不能回滚。

  对于在工做单元以内的 MQPUT 操做,因为交易隔离机制,在 COMMIT 以前,其余程序是看不到这条消息的,这点在编程中须要注意。

  有害消息的处理

  因为 MQ 消息处理有事务特性,若是队列里某条消息数据结构存在问题,程序处理它时会发生失败,这条消息会被自动回滚到队列中,下次当它再被读出时,又可能发生失败,这种状况若是没有被意识到,可能会引起严重的错误循环。为防止这种状况的发生,MQ 在消息头 MQMD 里设计了 BackoutCount 字段,若是某个消息是在同步点控制之下读取的,而且因为某种缘由消息被回滚,消息描述符中的 BackoutCount 字段的值将被加1,良好的程序设计须要判断该数值,若是它大于某个阈值,则须要使用其它手段来处理该消息,好比再也不对数据进行分析直接放到某个问题队列里去。在处理该消息的应用中,能够将其与设定的阈值作比较,这时,阈值会被写死在程序中,为了提升其灵活性,还可使用队列的 BOTHRESH 和 BOQNAME 属性。这样,在例外处理中,利用 MQINQ 查询获得阈值 BOTHRESH 的大小,若是超出,能够将消息转发到 BOQNAME 指定的队列中,继而对该队列进行相应的处理。这种方法大大加强了应用程序的灵活性。

  异步通讯下保持数据一致性的设计

  在使用 MQ 进行同步通信的程序设计时,会碰到原来可能会作单一 UOW 的应用,在MQ 的异步应用设计下要划分红若干个 UOW:发送程序 PUT 到队列里是一个 UOW,接收程序 GET 又是一个 UOW,这就涉及到如何在多 UOW 下保证数据总体的一致性的问题。这种需求,通常能够经过灵活地使用 MQ 提供的消息生命周期功能和应用的冲正逻辑进行配合。在典型的状况下:

  假设请求系统向服务系统发出请求,调用服务系统上的某个数据改动交易,在请求系统作MQPUT 时,首先要设置请求消息的生命周期 T1,而且在消息到期时将消息丢弃,若是在消息发走以前消息过时,它就会在进入通道以前,被 MQ 系统丢弃;若是消息到达目的地以后,在被对方应用程序取走以前消息过时,它也将被 MQ 系统丢弃。请求系统发送请求后,到应答队列里去取结果,若是时间 T1 已过,数据还没有返回,就给用户显示"交易失败,超时"信息。

  在服务系统上,应答程序处理完请求,修改数据库后,返回结果消息,结果消息也设置生命周期T2(T2<T1),与请求消息不一样的是,咱们设置在消息到期时发送一个超时报告,将结果消息转发到另一个特定的队列中,在这个队列上用触发器挂一个冲正程序,这个程序对于全部超时而没有被请求系统的程序读走的消息,发动冲正,也就是把前台没有收到返回的交易都恢复回去。

  在这种设计下,若是请求系统发送后,交易数据没有被应答系统读走就超时了,请求系统可以正确显示"交易失败,超时",这条请求信息会被系统超时抛弃;若是应答系统已经读取并完成了数据更改,因为意外缘由应答消息没有被请求方读到,请求方显示"交易失败,超时"后将不会再读这条应答消息,它必将过时,过时后被系统复制到冲正队列,触发自动冲正,恢复了数据,整个的效果也是交易失败,数据没有改动。经过这样的设计,能够看到数据一致性可以很好地保持。

  不一样系统平台间编码转换

  IBM 主机与分布式系统的字符编码方式不一样,一方面是基于 EBCDIC 的编码方式,一方面是ASCII 的编码方式,在企业级应用中常常要考虑编码转换的实现方式。MQ 提供丰富的编码转换功能,它能够根据消息数据包中消息头里对消息内容编码格式的描述,和宿主系统 MQ 定义的 CCSID(应用程序中能够对系统默认的 CCSID 进行覆盖)自动进行 EBCDIC、ASCII 与 UNICOD 等编码之间的转换。MQ 编码转换对中文也有丰富的支持,不但能够支持 GBK2312 的 6000 多汉字,并且能够支持 GBK 的更大的字库。一般在中文环境下,主机使用 CCSID1388,对应的分布式系统 CCSID 是 1386。

  MQ 的编码转换,实现时有两种方式,一种是在发送通道上完成的自动转码,另外一种是在MQGET 时使用参数指定 MQ 帮助转码。经过通道的转码,是在定义发送通道时给定 CONVERT(YES) 或 CONVERT(NO) 参数,若是 CONVERT(YES),MQ 会在传递消息时,根据两个 MQ 服务器的 CCSID 进行编码转换。在 MQGET 时的转码,能够在 GetMsgOpts 里设置 MQGMO_CONVERT 参数,这样执行 MQGET 时,系统会自动根据消息 MQMD 里的 CodedCharSetId 值和本地 MQ 服务器的 CCSID 进行转换。实际实施中,后一种方式使用的更多,由于这时应用程序能够选择是读取转码后的数据仍是原始数据,若是在通道上已经转了,接收方就不能看到原始编码的数据了。

  触发器类型与并发处理

  MQ 支持触发器,触发器定义在某个队列上,当符合触发条件的事件发生时,MQ 会自动运行触发器程序,触发器程序能够是批量程序(须要 MA12 SupportPac 的支持)、CICS 联机程序或IMS TS 管理的联机程序。

  触发器类型

  为了平衡消息平均等待时间和处理效率,MQ 提供三种不一样的触发方式:EVERY、DEPTH 和FIRST。

  EVERY 触发方式在每一个新消息到达时被知足,会运行一次应用程序。这时消息平均等待时间最小,但在消息大量到达时会启动大量触发器进程,每一个进程只处理一个消息,效率较低。

  DEPTH 触发方式在队列中积累的消息到达必定数量时被知足,会运行一次应用程序。这个进程会处理全部消息直到队列空,处理效率高但消息平均等待时间比较长。

  FIRST 触发条件在队列的深度从 0 到 1 时获得知足,运行一次应用程序,这个进程也要处理全部消息直到队列空。它是 EVERY 和 DEPTH 之间的一个平衡,即能提供比较高的处理效率,又不会让一些消息等待太长,于是是一种经常使用方式。

  高性能联机触发程序设计

  在大企业应用设计时,必定要考虑到程序在大交易量时的表现,必定要提供最高的处理效率。若是考虑触发程序是 CICS 里的一支联机交易程序,咱们会发现,若是使用 EVERY 触发方式,因为没法控制消息什么时间到来,系统会随着消息到达的多少出现明显的"震荡",并且当大量数据同时到达时会在 CICS 里启动过多的处理程序,若是不加控制甚至会把 CICS 压垮。所以 EVERY 方式一般不是 CICS 程序的好的触发方式。而当使用 FIRST 或 DEPTH 方式时,每一时刻被 MQ 调起的触发程序只有一支,并发度明显不足,在交易量大时,有可能造成性能瓶颈。在大交易量环境下进行触发器程序设计时必定要有新的思路。

  具体设计的作法能够有许多,这里介绍一种比较简洁的方法:控制程序/消息处理程序方式。控制程序是定义在 MQ 中的触发程序,它的功能是负责启动必定数量的消息处理程序并能够动态调整,在系统中任什么时候刻只有一个控制程序在运行,它只使用 MQINQ 获得队列深度和队列上消息处理程序数目等信息,它不直接读取队列里的消息数据。消息处理程序是真正的消息处理者,每一个消息处理程序从输入队列读取数据,通过处理后发送到输出队列,它不停地逐条处理消息,直到队列空,消息处理程序的数目能够由控制程序控制。控制程序要使用一些特定的算法来决定和控制消息处理程序的数目,它能够经过 MQINQ 命令获得队列的深度和有多少个消息处理程序在打开这个队列的信息,控制程序能够设置处理程序数目的初始值、增加值和最高值,在第一次启动时,启动初始值数目的处理程序,而后控制程序进入休眠(EXEC CICS DELAY),若干秒后控制程序苏醒,再次检查队列深度和消息处理程序数目,若是队列深度仍在增长,代表处理能力不足,这是就启动更多的消息处理程序,如此往复直到队列深度是 0,控制程序退出。

相关文章
相关标签/搜索