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。应用程序可以使用这个字段来指定发送消息的格式名,接收方根据这个名字对消息体做出不同解释。

  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,控制程序退出。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值