转自;http://www.ibm.com/developerworks/cn/websphere/library/techarticles/1001_xiao/1001_xiao.html
IBM® WebSphere® MQ 是 IBM 提供的用于商业消息和队列传递的出色中间件。消息队列是一种程序到程序的通信方法,其中应用程序通过队列发送和检索特定于应用程序的数据,无需建立私有的、专用的逻辑连接。一条消息仅是对使用它的应用程序有意义的一串字节,而队列则是消息的容器。
本文描述 WebSphere MQ 消息格式的演变以及在消息排队过程中由 WebSphere MQ 自动对消息进行的更改。您需要对基于 WebSphere MQ 的应用程序开发有一定程度的了解。
WebSphere MQ 消息具有典型的 “头-主体(header-body)” 格式。在 WebSphere MQ 的不同版本中,头和主体随内容和概念而有所不同。不过,从第一版到 WebSphere MQ V6(此后简称为 V6),消息格式出现小小的变化。在 WebSphere MQ V7(此后简称为 V7)中,为了增加与 Java™ Message Service (JMS) 应用程序的兼容性,消息格式发生了显著的变化。
在 V6 及之前版本中,WebSphere MQ 消息的两个组成部分是:
- 消息描述符(消息头)
- 应用程序数据(消息体)
图 1 显示了 V6 及之前版本的消息格式:
消息描述符与队列管理器内驻留在队列上的每个消息相关。它标识消息且包含控制信息,比如消息类型和由发送应用程序分配的优先级。应用程序数据部分包含所有实际消息数据(有效负载)和任何附加头,比如传输头或 IMS 信息头。除最大长度之外,WebSphere MQ 不对应用程序数据施加任何限制,根据 WebSphere MQ 版本的不同,最大长度的变化范围是 4 到 100MB。对队列管理器、通道和单个队列也可定义最大消息长度。
V6 及之前版本的消息具有相同的组成结构,但是存在以下一些差异:
- 从 V5 开始消息描述符的属性开始改变 — 详细信息参见以下内容。
- 消息数据前面的附加头的类型更加丰富化。例如,从 V5 开始添加了消息描述符扩展(MQMDE),从 V6 开始添加了嵌入式 PCF 头(MQEPH)。
1. 消息描述符
消息描述符由一种 MQMD 结构定义,这种结构包含大量提供消息信息的字段。下面内容详细描述了这些属性中的其中两个。
-
版本
消息描述符有两个版本。自 V5 以来,可以对消息分段或分组。对于该新功能,需要在消息描述符中附加字段来保留分段和分组信息。 这种扩展结构即为消息描述符的当前版本 V2。V2 消息描述符与 V1 相同,但是它具有由 MQMDE 结构定义的附加字段:GroupId、MsgSeqNumber、Offset、MsgFlags 和 OriginalLength。
不支持这些功能的其他队列管理器(称作 V1 队列管理器)将附加信息看作数据。一个 V2 消息描述符一般相当于使用一个 V1 消息描述符并使用 MQMDE 结构为消息数据添加前缀。支持消息分段和分组的队列管理器被称作 V2 队列管理器。图 2 显示了 V1 与 V2 MQMD 之间的关系:
图 2. V1 与 V2 MQMD 之间的关系
-
格式
格式是消息发送者使用的名称,用于向接收者表明消息中数据的性质。队列管理器含有大量以 MQ 前缀命名的内置格式,比如 MQFMT_STRING。格式还可用于表示附加头(扩展)的存在。例如,如果消息描述符中 Format 字段的值是 MQFMT_CICS,它表示消息数据以 CICS 信息头 MQCIH 开始,后面是负载数据。负载数据的性质由 MQCIH 头的 Format 字段表明。图 3 显示了对 Format 的使用:
图 3. 使用 Format 表示消息中数据的性质
2. 应用程序数据
应用程序数据部分包含从一个程序发送到另一程序的真实数据,且其内容和结构由使用它的应用程序定义。一般地,应用程序数据仅包含对应用程序有意义的信息,因为消息描述符中的控制信息就已经足够了。不过在某些环境下需要更多控制信息并将其以附加头的形式作为负载数据的前缀,如上面图 1 所示。附加头可由应用程序针对特殊用途添加,比如发送消息到 IMS 网桥时添加 IMS 信息头(MQIIH)到应用程序数据。消息头有时也由 WebSphere MQ 添加,如下描述。
表 1 列出了由 WebSphere MQ 定义的可用作应用程序数据前缀的消息头及其格式名称。
| 消息头 | 消息头描述 | 格式名称 |
|---|---|---|
| MQCIH | CICS 信息头 | MQFMT_CICS |
| MQDLH | 死信头 | MQFMT_DEAD_LETTER_HEADER |
| MQDH | 分布列表(Distribution-list)头 | MQFMT_DIST_HEADER |
| MQEPH | 嵌入式 PCF 头 | MQFMT_××DED_PCF |
| MQIIH | IMS 信息头 | MQFMT_IMS |
| MQMDE | 消息描述符扩展 | MQFMT_MD_EXTENSION |
| MQCFH | PCF 头 | MQFMT_ADMIN / MQFMT_EVENT / MQFMT_PCF |
| MQRMH | 参考消息头 | MQFMT_REF_MSG_HEADER |
| MQRFH | 格式化头 | MQFMT_RF_HEADER |
| MQRFH2 | 版本 2 规则和格式化头 | MQFMT_RF_HEADER_2 |
| MQWIH | 工作信息头 | MQFMT_WORK_INFO_HEADER |
| MQXQH | 传输队列头 | MQFMT_XMIT_Q_HEADER |
我们可对附加头进行约束,即应用程序数据可以选择性地在负载数据前包含一个或多个消息头。以 WebSphere MQ V7 之前的发布或订阅应用程序的消息为例 — 消息数据可同时以 MQRFH1 和 MQRFH2 开头。如果在负载数据前添加多于一个消息头时,它们以同样的方式链接在一起,如上面图 3 Format 字段所示。
WebSphere MQ V7(此后简称 V7)引入了消息属性(来自 JMS)的概念,因此 WebSphere MQ 消息的两个组成部分变为:
- 消息属性(消息头)
- 应用程序数据(消息体)
V7 的消息格式如图 4 所示,图中还显示了消息属性与消息描述符之间的对应:
1. 消息属性
作为 Message Queue Interface (MQI) 的一部分,消息属性是 V7 的新增特性。新的 MQI 函数调用用于设置、查询和删除消息属性(MQSETMP,MQINQMP 和 MQDLTMP)。您也可以使用消息属性来包含业务数据或状态信息,而无需将其存储在应用程序数据中。WebSphere MQ 中消息属性的使用模仿 JMS 中属性的使用,从而使您能够在 JMS 环境中设置属性,然后在过程 WebSphere MQ 应用程序中检索它们,反之亦然。
消息属性随 WebSphere MQ 队列管理器中的任何消息流动,每个消息属性都包括一个文本名称和一个特定类型的值。消息管理器的大小不能超过队列管理器的 MaxPropertiesLength 设置。消息属性不计入队列和队列管理器的消息长度中,不过它们计入被队列管理器认为是属性的长度中。消息属性的长度限值是 100 MB,其中不包括每个消息的消息描述符或扩展。
2. 以 MQRFH2 表示的消息属性
消息属性可以以 MQRFH2 元素的形式表示。图 5 显示了 MQRFH2 头的结构:
NameValueData 字段中的字符串包括含有零或更多属性的单一文件夹。如果 content=properties 元素包含在文件夹 start 标记中,则文件夹的内容会被看作是消息属性。如果消息头中的每个文件夹都包含属性,MQRFH2 头字段本身会被计入到消息属性的长度中,并成为消息属性的一部分。否则,头字段被计入消息长度中,成为应用程序数据的一部分。因此,应用程序可使用大于 MaxMsgLength 值的缓冲区放置消息,前提是该缓冲区包含属性。
3. 作为属性的消息描述符字段
除了 StructId 和 Version 之外,大多数消息描述符字段可被看作属性。属性名可通过添加前缀到消息描述符字段名来构建,例如 Root.MQMD.Priority。这里 Priority 是消息描述符中的一个字段。消息描述符字段从来不像其他属性一样以 MQRFH2 头的形式表示。如果以 MQMDE 开头的消息数据得到队列管理器的重视,那么使用与上述描述相同的语义可访问 MQMDE 字段。在本例中,从属性角度来看,MQMDE 字段从逻辑上被看作是 MQMD 的一部分。
4. 流向上一版本的消息
当消息流向 V7 版本之前的队列管理器时,除了那些在消息描述符中的属性之外,消息属性都被看作是消息数据并计入消息长度中:
因此,对于流向 V7 之前的系统的通道,其 MaxMsgLength 属性的值应提高,以补偿每个消息本来可能发送更多数据的损失。
当消息通过 WebSphere MQ 从一个程序流向另一程序时,为将其传送到目的地,可能需要对消息进行更改。本节探讨在消息传输中 WebSphere MQ 更改消息的不同方式。这些更改由队列管理器、消息通道代理程序或其他 WebSphere MQ 基础设施自动完成,且对应用程序透明。
我们知道,WebSphere MQ 可划分为包含控制信息的消息头和包含负载数据的消息数据。负载数据一般不由队列管理器更改,除非需要对其进行数据转换。多数情况下,对消息的更改是对控制信息(消息头)进行的,比如附加头的添加或对已有头的修改。
在某些情况下,WebSphere MQ 通过对消息数据附加更多头前缀并更改消息描述符的 Format 字段来向消息添加额外控制信息。WebSphere MQ 在以下四种情况下向消息添加头信息:
情况 1. 当消息位于远程队列时
当消息位于远程队列时,WebSphere MQ 在将消息置于传输队列之前向其添加传输头结构(MQXQH)。传输头包括目标队列名称(RemoteQName)和队列管理器名称(RemoteQMgrName),即寻址信息。该信息用于传送消息到网络中正确的目的地。表 2 列出了 MQXQH 中的一些主要字段:
| 字段 | 描述 |
|---|---|
| RemoteQName | 目标队列的名称 |
| RemoteQMgrName | 目标队列管理器的名称 |
| MsgDesc | 原始消息描述符(嵌入式) |
除了寻址信息之外,嵌入式消息描述符(MsgDesc)也作为消息数据的一部分存储在 MQXQH 结构内。此描述符与由输入端应用程序置入的原始消息描述符一同产生。另一种消息描述符在将消息置于传输队列时由队列管理器生成,它与消息数据独立存储,称作独立的消息描述符。因此,传输队列上的一条消息有两个消息描述符,如图 7 所示:
嵌入式消息描述符始终是 V1 MQMD 结构。如果应用程序置入的消息对于 MQMD 中的一个或多个 V2 字段有非默认值,则 MQMDE 结构紧跟在 MQXQH 后面,并位于应用程序消息数据的前面,如上面图 7 所示。
当消息到达其目标队列时,传输头与独立的消息描述符相分离,于是嵌入式消息描述符便成为消息特有的消息描述符 — 它不再是消息数据的一部分。
情况 2. 当队列管理器不能传递消息时
在这种情况下,WebSphere MQ 添加死信头结构(MQDLH)到消息中,并将其放到死信(未传递消息)队列上。另外,为表明消息包含 MQDLH 结构,消息描述符(MQMD)的 Format 字段被更改。该结构包含目标队列的名称和将消息放在死信队列上的原因。表 3 列出了 MQDLH 中的主要字段:
| 字段 | 描述 |
|---|---|
| Reason | 到达死信队列的原因消息 |
| DestQName | 原始目标队列的名称 |
| DestQMgrName | 原始队列管理器的名称 |
| Format | MQDLH 后面的数据的格式名称 |
| PutApplType | 放置消息到死信队列的应用程序类型 |
| PutApplName | 放置消息到死信队列的应用程序名称 |
消息可由队列管理器、消息通道、消息通道代理程序(MCA)和应用程序放到死信队列上。死信队列上的所有消息必须以 MQDLH 为前缀,如果由于加入 MQDLH 头而使消息相对于队列太长,可将消息截短。
从死信队列获取消息的应用程序必须确保消息以 MQDLH 结构开头。应用程序可通过检查消息描述符中的 Format 字段确定其是否含有 MQDLH 结构。如果字段含有 MQFMT_DEAD_LETTER_HEADER 值,则消息数据以 MQDLH 结构开头。
情况 3. 当发送一条消息到多个目标队列时
在这种情况下,WebSphere MQ 添加分布式头结构(MQDH)到消息中,并将其放在相关的传输队列中。因此,应用程序消息数据以 MQXQH 和 MQDH 结构为前缀。当消息是存储在传输队列中的分布列表消息时,MQDH 会描述该消息中的附加数据。分布列表消息是发送到多个目标队列的消息。附加数据包括 MQDH 结构和紧接其后的 MQOR 记录数组和 MQPMR 记录数据。表 4 列出了 MQDH 中的主要字段:
| 字段 | 描述 |
|---|---|
| StrucLength | MQDH 结构与其后的记录的总长度 |
| Format | MQPMR 记录数组之后的数据的格式名称 |
| PutMsgRecFields | 表明有哪些 MQPMR 字段存在的标志 |
| RecsPresent | 当前对象记录数 |
| ObjectRecOffset | 第一个对象记录与 MQDH 初始位置的偏移量 |
| PutMsgRecOffset | 第一个置入消息(put-message)记录与 MQDH 初始位置的偏移量 |
传输队列上分布列表消息的消息数据排列顺序如下:
- MQXQH 结构
- MQDH 结构与 MQOR 和 MQPMR 记录数组
- 应用程序消息数据(负载数据)
传输队列上分布列表消息的结构如图 8 所示:
StrucLength 是从 MQDH 结构初始位置到 MQOR 和 MQPMR 记录数组后面的消息数据的初始位置之间的字节数。 ObjectRecOffset 为包含目标队列名称的 MQOR 对象记录数组中第一个记录提供字节偏移量。 PutMsgRecOffset 为包含消息属性的 MQPMR 置入消息记录数组的第一个记录提供字节偏移量。
情况 4. 当消息是组中的一个片段或一条消息时
在这种情况下,WebSphere MQ 可能会添加消息描述符扩展(MQMDE)结构。
消息分段对于应用程序可以是透明的。如果允许,队列管理器会在消息太大以至于队列不能容纳时将消息分段。如果应用程序使用的是 V1 MQMD,队列管理器会自动添加 MQMDE 结构到消息数据中。如上所述,MQMDE 结构包含那些存在于 V2 MQMD 中而 V1 MQMD 没有的 MQMD 字段,因为 V1 MQMD 中存储分段和分组信息。
除了添加额外头到信息中之外,WebSphere MQ 还可能在消息在发送和接收消息的应用程序之间传输时更改消息中已有头的特定字段:
1. 所有前缀头中的 Format 字段
如上所述,WebSphere MQ 在特定环境下添加头信息到消息中。一旦添加了额外的头,其前缀头结构的 Format 字段也被 WebSphere MQ 更新,以表明新消息头的类型。例如,当 WebSphere MQ 在消息的消息描述符后面添加传输头时,MQMD 的 Format 字段被更改为 MQFMT_XMIT_Q_HEADER。
2. 传输头中包含寻址信息的字段
在双向通信期间(在 WebSphere MQ 中,双向通信是指从一个队列管理器发送消息到另一个队列管理器),当队列管理器传送消息时,如果使用了队列管理器别名,它可能会更改与消息一同传输的传输头的寻址信息(RemoteQName,RemoteQMgrName)。
来自相邻系统的消息包含目标队列管理器和传输头中队列的物理名称(对名称解析后得到的名称)。如果队列管理器别名定义与传输头中引用的同一队列管理器名均存在,那么用于传递消息的本地队列管理器会用来自别名定义的 RQMNAME 覆盖 RemoteQMgrName 字段。
3. 消息描述符中的 ReplyToQ, ReplyToQMgr
事实上,对这两个字段的更改是在应用程序将消息放到队列之前进行的。如果应用程序将消息放到为应答消息而提供 reply-to 队列名称(ReplyToQ)的队列中,同时队列管理器名称(ReplyToMgr)为空,本地队列管理器会通过用与 reply-to 队列相同的名称(reply-to 别名)检查远程队列定义来响应空的队列管理器名称:
- 如果找不到任何结果,队列管理器会在保持 ReplyToQ 不变的情况下在消息描述符的 ReplyToQMgr 字段中置入自己的名称。
- 如果定义存在,队列管理器从定义中抽取队列名称和队列管理器名称,然后将其放入消息描述符的 reply-to 字段中 — 即替换 ReplyToQ 和 ReplyToQMgr 的值。
4. 消息描述符中的 Context 字段
消息上下文信息存储在消息描述符的 context 字段中。有两种类型的消息上下文:身份上下文(identity context)和发送者上下文(origin context)。V7 中为消息属性添加了一种新的上下文信息:用户上下文(user context)。图 5 列出了消息描述符中的 context 字段:
| 上下文类型 | 字段和描述 |
|---|---|
| 身份上下文 |
UserIdentifier:发起消息的应用程序的用户标识符。 AccountingToken:帐户标志 ApplIdentityData:与身份相关的应用程序数据 |
| 发送者上下文 |
PutApplType:放置消息的应用程序的类型 PutApplName:放置消息的应用程序的名称。 PutDate:放置消息的日期。 PutTime:放置消息的时间。 ApplOriginData:与发起消息相关的应用程序数据。 |
| 用户上下文 |
被应用程序识别为用户上下文的任何属性。 |
身份和发送者上下文的多数字段通常由队列管理器提供。当然,拥有合适授权的应用程序可提供其自己的上下文:
- 身份上下文信息标识首次将消息放入队列的应用程序的用户。队列管理器根据应用程序的运行环境填充 UserIdentifier 和 AccountingToken 字段。
- 发送者上下文信息描述将消息放入当前存储消息的队列中的应用程序。以下字段通常由队列管理器指定:PutApplType、PutApplName、PutDate 和 PutTime。
- 用户上下文不由队列管理器自动设定。
5. 消息描述符(V2)中的 Segmenting/Grouping 字段
如果允许,队列管理器会将由于太长而难以发送的消息分成大量更小的消息。同时,队列管理器在消息描述符(如果是 V2 的话)中设置以下分段/分组字段:GroupId、MsgSeqNumber、Offset、MsgFlags 和 OriginalLength。如上所述,如果消息描述符是 V1,MQMDE 结构由队列管理器添加。
如果发送和接收应用程序之间的数据格式不同,就需要进行数据转换。因为 WebSphere MQ 必须能够了解消息描述符(MQMD)的内容,不管创建它的平台是什么,MQMD 均由系统自动转换。不过,对于消息的应用程序数据部分,情况有点复杂:
- 使用由 WebSphere MQ 定义的格式的数据(内置格式) — 表 1 所列格式是所有内置格式的子集。这些格式中的消息数据可由队列管理器自动从一个编码字符集转换为另一个编码字符集,前提是两种字符集都与单个语言或一组类似语言相关。
- 使用用户定义格式的数据 — 队列管理器不能转换用户定义格式的数据。应用程序必须为这样的格式提供数据转换出口。
综上所述,只有内置格式的消息数据在需要数据转换时才能由 WebSphere MQ 自动进行转换。
本文描述了 WebSphere MQ 消息格式和 WebSphere MQ 在消息排队期间进行的消息更改。它显示了不同版本的 WebSphere MQ 消息格式在概念上的不同之处和组成上的相似之处,使用了一种包含两个组成部分的头-主体结构:
- 消息描述符和应用程序数据(V6 及之前的所有版本)
- 消息属性和应用程序数据(V7 版本)
一条消息在从一个应用程序传送到另一应用程序时,WebSphere MQ 可通过三种方式对其进行更改:
- 添加额外的头(包含更多控制信息)。
- 更改消息中已有头的字段。
- 执行数据转换。





