存货周转次数越高越好:结合范例看MQ应用系统设计思路

来源:百度文库 编辑:中财网 时间:2024/05/06 02:35:26

 

简介: MQ软件中,有丰富的范例程序,通过这些程序,我们不但可以学习到MQ代码的编写方法,而且可以从范例的结构中学习到MQ下应用程序设计的方法。

标记本文!

发布日期: 2006 年 4 月 24 日
级别: 初级
访问情况 364 次浏览
建议: 0 (添加评论)

平均分 (共 0 个评分 )

 

1 前言

目前,国内使用IBM主机的用户,进行分布式应用系统设计时,通常使用的是一种在CICS系统支持下的远程系统调用方式,这种方式在进行不同系统间应用集成时有一定的局限性。IBM提供的消息中间件产品WebSphere MQ 为构造以同步或异步方式实现的分布式应用提供了一种松耦合的应用方法,它简化了应用之间数据的传输,屏蔽底层异构操作系统和网络平台,提供一致的通讯标准和应用开发,确保分布式计算网络环境下可靠的、跨平台的信息传输和数据交换。它基于消息队列的存储-转发机制,并提供特有的异步传输机制,能够基于消息传输和异步事务处理实现应用整合与数据交换。

基于MQ应用是一种面向消息的处理模式异步,与我们通常使用的同步处理模式有很大的不同,这里我们结合MQ系统提供的一个模拟银行信贷审核应用的范例,介绍这种消息驱动的应用设计模式的概况,并对这种模式下需要考虑的各种因素进行详细的分析介绍,希望能使大家能充分了解掌握MQ下应用设计的基本方法。


回页首

2 MQ应用范例

MQ软件中,有丰富的范例程序,通过这些程序,我们不但可以学习到MQ代码的编写方法,而且可以从范例的结构中学习到MQ下应用程序设计的方法。

Credit Check程序是MQ for z/OS自带的一个的应用范例,它使用MQ和CICS模拟了一个银行信贷申请的审核应用,通过对这个范例的学习,我们可以了解到如何进行消息驱动模式的应用系统结构设计,如何用MQ设计混合有联机/批量交易的应用系统,如何将CICS和MQ联合起来完成更丰富的应用功能。

2.1 MQ范例程序Credit Check介绍

这个应用所演示的业务操作,是银行对一个申请贷款的客户进行风险评估的业务过程,银行员工从CICS系统3270界面输入查询请求,后台程序首先检查客户的平均存款记录,对于金额大于1万的请求,还要对客户更多的信息进行附加检查比如客户信用卡记录,储蓄帐户、以往的还款记录等,最后所有检查的结果合在一起返回给银行员工。。 的这个例子可以通过两种方式对贷款申请进行处理:

  • 对于上门申请的情况,银行员工可以联机地对帐户和风险信息进行查询。
  • 对于书面申请的情况,银行员工可以先发送帐户和风险查询请求,然后在晚些时候再看结果,进行处理。

例子中的财务和安全处理都尽量进行了简化,以突出消息队列技术是如何应用的。

Credit Check可以在CICS环境中运行,同时也提供了一套在IMS(TM)环境中运行的代码。CICS环境下提供了C和COBOL两种语言的样例代码,IMS只有C语言的代码。Credit Check可以修改配置,把程序部署在不同的队列管理器和CICS系统下,分布式地运行。范例可以在MQ的安装Dataset里找到,由以下文件组成:



Credit Check范例使用一个VSAM文件来模拟客户的帐户信息,这个文件内容如下:



如果输入的是这些帐号里的一个,应用程序能够查询到这些帐户的平均余额和和信用等级。 Credit Check例子的准备和运行,请参考MQ for z/OS V6的《Application Programming Guide》第29章" Sample programs for WebSphere MQ for z/OS"的"The Credit Check sample"部分说明。注意LINKEDIT时要用CICS下的存根程序CSQCSTUB

1. 例子准备好后就可以到CICS里运行。C语言的版本CICS交易码是MCB1,COBOL语言的版本CICS交易码是MVB1。交易启动后首先选择查询方式,是Immediate还是batch模式。接下来Immediate 和batch查询的界面是相似的,下面就是一个例子。


CSQ4CB2   IBM WebSphere MQ for z/OS - Sample Programs            Credit Check - Immediate Inquiry            Specify details of the request, then press Enter.            Name . . . . . . . . .            Social security number            Bank account name  . .            Account number . . . .  3501676212            Amount requested . . .  90000            F1=Help  F3=Exit  F5=Make another inquiry            

在屏幕上输入帐号和贷款金额进行查询就会返回结果,如下就是一个联机查询的例子:


            CSQ4CB2   IBM WebSphere MQ for z/OS - Sample Programs            Credit Check - Immediate Inquiry            Specify details of the request, then press Enter.            Name . . . . . . . . .            Social security number            Bank account name  . .            Account number . . . .  3501676212            Amount requested . . .  090000            Response from CHECKING ACCOUNT for name : JONES J B            Opened 1987/05/30 , 3-month average balance = 000012.57            Credit worthiness index - BBB            Response from CSQ4SAMP.B5.C.MESSAGES            Loan amount 090000 for            Credit worthiness index - Fair            Response from CSQ4SAMP.B6.C.MESSAGES            Loan amount 090000 for            Credit worthiness index - Fair            Response from CSQ4SAMP.B7.C.MESSAGES            Loan amount 090000 for            Credit worthiness index - Fair            INQUIRY COMPLETE            F1=Help  F3=Exit  F5=Make another inquiry            

2.2 范例的设计

下面介绍Credit Check应用的各个程序的功能、结构和相互之间的连接。下图画出了组成应用的所有程序,以及这些程序间的通信队列。图中所示的队列名字,为了比较简洁,都省略了CSQ4SAMP的前缀。



2.2.1 用户界面程序(CSQ4CVB1/CSQ4CCB1)

当你启动对话交易MVB1时,首先会启动界面程序CSQ4CVB1/CSQ4CCB1,这个程序把查询请求发送到MQ队列CSQ4SAMP.B2.INQUIRY中去,然后从某个返回队列取得查询结果数据,这个返回队列根据查询的模式是不同的,如果在规定的时间内没有得到响应,报告错误。在请求界面里,用户即可以发送联机查询,又可以发送批量查询:

  • 对于联机查询,程序创建一个临时动态队列,作为查询数据的返回队列,这样做让每一个查询都拥有一个自己的返回队列。
  • 对于批量查询,程序到MQ队列CSQ4SAMP.B2.RESPONSE中去读取查询结果,所有批量查询结果都放到这个队列,这样做是为的简化程序,实际设计时也可以为每个柜员设立单独返回的队列,而不把所有结果合到一起。

用联机方式和批量方式发送的请求,MQ消息的属性不同:

  • 对于批量方式,消息的优先级是比较低的,所以它们会在所有联机查询处理完了以后才能得到处理;另外,批量方式发送的消息是持久的,当应用或队列管理器重起后,查询请求还在。
  • 对于联机方式,消息优先级是比较高的,它们会比所有批量方式进来的查询得到更快的处理;同时,这些消息是非持久的,这会减少系统的压力,但当队列管理器重起后,查询请求就被抛弃了。

另外,在所有方式下,消息的优先级是在应用中继承的:较高优先级消息的返回消息也是较高优先级的。

2.2.2 信用查询主程序(CSQ4CVB2/CSQ4CCB2)

信用查询主程序(为了方便,以后会称它为CAM,Credit Application Manager) 完成Credit Check应用的主要控制过程。 它是被MQ CICS adapter的触发监控程序CKTI (由WebSphere MQ for z/OS提供)启动的,一旦有消息到达队列CSQ4SAMP.B2.INQUIRY 或 CSQ4SAMP.B2.REPLY.n(n是一个序号)时,CKTI会自动调起这个程序运行。CSQ4CVB2/CSQ4CCB2用CICS RETRIEVE命令从CKTI得到的MQTM结构里能够识别是被哪个队列里的消息出发的。

CAM使用CSQ4SAMP.B2.WAITING.n格式的队列名来存储要处理的查询信息,应答消息的队列名和它是对应的,比如队列CSQ4SAMP.B2.WAITING.3里存放某个特定查询的输入数据,那么跟这个查询相关的所有应答消息,都会放到CSQ4SAMP.B2.REPLY.3队列。

等待队列(CSQ4SAMP.B2.WAITING.n)被CAM用来在查询部分完成时恢复数据用的,CAM能够从中恢复出尚未完全完成的程序的状态。凡是CAM发出的查询和收到的返回,都拷贝到等待队列里,在最后返回给前台时把相关的消息清掉,这样一个前台查询所涉及的所有消息都保存在这里,CAM启动时可以从等待队列里面恢复当前有多少个正在进行的查询,每个查询分发出多少附加查询,这些附加查询返回了多少。

对每个查询消息,CAM在内存的查询记录表(Inquiry Record Table -- IRT)里初始化一条记录,这条记录包括:

  • 查询消息的MsgId
  • 在ReplyExp域填记希望收回的应答数目(跟发出请求的数目一样)
  • 在ReplyRec域填记已经收回的应答数目 (初始值是0)
  • 在PropsOut域填记一个标记表明是否希望一个propagation message

CAM用这个结构保存目前正进行的查询的状态,每次CAM程序启动时,当前的状态可以用等待队列里的消息恢复出来。

CAM启动时首先打开所要使用的各种队列,然后浏览等待队列,恢复IRT表,接着就进入消息处理的循环:从B2.INQUIRY里读取数据,按照下面的逻辑处理,一直到IRT表满或B2.INQUIRY里没有消息。如果是IRT满,它将去读B2.REPLY.n,按照下文所述的逻辑处理返回消息,当一个查询完全结束,向前台返回时会在IRT里减少一条记录,然后再去B2.INQUIRY读数据。这样下去一直到B2.INQUIRY空,由于使用SIGNAL的方式进行MQGET,队列空时,一个信号已被设置,下面去处理B2.REPLY.n里的消息,直到也处理空,由于也是使用SIGNAL方式读,也有一个信号被设置,程序开始EXTERNAL WAIT。如果有这两种队列上有新消息到达,任意一个信号被满足时,程序被唤醒,继续上面循环处理,一直到某次等待到了任意一个信号超时(例子里用的33秒),CAM程序退出,等待下次被CKTI触发。

2.2.2.1 启动逻辑

如果CAM是被队列CSQ4SAMP.B2.INQUIRY里的消息触发的,它以共享方式打开这个队列。然后它试着去打开后续查询的返回队列CSQ4SAMP.B2.REPLY.n ,找到可用的最小的n值,如果没有可用的队列,程序结束。

如果CAM是被队列CSQ4SAMP.B2.REPLY.n上的消息触发的,它以独占的方式打开这个队列。如果返回码说明队列已经被其他程序打开了,CAM正常退出;如果发生其他错误,CAM记录这个错误然后退出。CAM打开相应的等待队列(CSQ4SAMP.B2.WAITING.n)和请求队列(CSQ4SAMP.B2.INQUIRY),然后开始读取和处理消息。

为了代码简洁,队列的名字都是写死在程序里的,在实际的应用环境中,可以放在配置表里。

2.2.2.2 读取消息

CAM首先用带MQGMO_SET_SIGNAL 选项的MQGET命令到请求队列(B2.INQUIRY)里去读取消息,如果当时有消息,就会被读出来处理,如果没有消息,就会有一个信号被设置。

然后CAM用同样的参数用MQGET到应答队列(B2.REPLY.n)里读取消息,如果当时有消息,就会被读出来处理,否则也会设置一个信号。

当两个信号都设置以后,程序进入等待状态,直到任意一个信号被发送过来。如果有信号发送过来,表明有消息可用了,这个消息就会被读出来进行处理。如果信号过期了或队列管理器结束,本程序终止。

2.2.2.3 处理消息

CAM可能得到的消息有以下四种:

  • 信用查询请求消息(B2.INQUIRY)
  • 应答消息(B2.REPLY.n,包括帐户查询程序、附加查询分发程序和附加查询处理程序的返回)
  • 传播消息(propagation message,附加查询分发程序记载分发数量的消息)
  • 未知或意外消息(以上几种之外,在队列上收到的无法处理的意外消息)

对于不同的消息CAM会如下处理:

信用查询请求消息(Inquiry message)

信用查询请求消息(B2.INQUIRY中)是从用户界面程序发送过来的,每个信用查询会发送一条消息。对于所有查询,CAM首先查询客户存款帐户的平均余额,它向别名队列CSQ4SAMP.B2.OUTPUT.ALIAS里发送一条消息,这个别名实际指向队列CSQ4SAMP.B3.MESSAGES,这条消息将会被帐户查询程序CSQ4CVB3/ CSQ4CCB3处理。当CAM发送消息到这个别名队列时,它把消息MQMD的ReplyToQ设为CSQ4SAMP.B2.REPLY.n。这里使用别名,是为了可以方便地对程序CSQ4CVB3/ CSQ4CCB3进行替换,替换为监视另一个队列的其他程序,只要把别名队列重新定义就可以了;同时还可以为别名队列和实际队列设计不同的授权控制,进行详细的权限管理。

如果用户申请的贷款数量大于10000,CAM还要检查其他系统的数据记录。它要向队列CSQ4SAMP.B4.MESSAGES里发送一条消息,这个消息会被分发程序CSQ4CVB4/ CSQ4CCB4处理,这个程序把消息转发给若干附加查询处理程序进行更多的检查,比如检查客户信用卡记录,储蓄帐户、以往的还款记录等其他信用信息。这些程序的返回信息,都会返回到ReplyToQ指定的队列里去,另外分发程序还会写一条消息到这个队列,说明把这个请求分发给多少程序去检查。

CSQ4CVB4/ CSQ4CCB4程序只是简单地把原请求消息原样转给附加查询处理程序,在实际环境中,分发程序可能会对消息进行格式处理后再发给其他检查程序,以适应那些程序已有的接口格式。

这里所用到的所有队列,都可以是定义在其他系统上的远程队列,相应地,处理这些消息的应用程序也在远端运行。如果应用要与其他系统上的程序联合工作就会用到这种模式。

对每个查询消息,CAM在内存的查询记录表(Inquiry Record Table -- IRT)里初始化一条记录,这条记录包括:

  • 查询消息的MsgId
  • 在ReplyExp域填记希望收回的应答数目(跟发出请求的数目一样)
  • 在ReplyRec域填记已经收回的应答数目 (初始值是0)
  • 在PropsOut域填记一个标记表明是否希望一个传播消息(propagation message)

CAM把信用查询请求消息(来自B2.INQUIRY)拷贝到等待队列(B2.WAITING.n)时设置以下参数:

  • Priority 设为 3
  • CorrelId 设为信用查询请求消息的MsgId
  • 其他MQMD域跟信用查询请求消息的一样

传播消息(Propagation message)

传播消息是被分发程序CSQ4CVB4/ CSQ4CCB4写到B2.REPLY.n里的,内容是记载分发程序把查询转发到几个处理程序。读到这种消息将如下处理:

1. 把转发程序个数加到IRT表中记载这个请求的记录的ReplyExp 域中,程序个数在消息中可以得到。

2. 把IRT表中这个请求的记录的ReplyRec字段加1

3. 把IRT表中这个请求的记录的PropsOut字段减1

4. 把这个消息拷贝到等待队列(waiting queue)上。放时把Priority 设置成2,其他MQMD域与原来一样。

应答消息(Reply message)

应答消息是被帐户查询程序、附加查询处理程序写到队列B2.REPLY.n中的。应答消息包含有对帐户查询程序CSQ4CVB3/ CSQ4CCB3和分发程序转发给的代理查询程序发出请求的应答。应答消息如下处理:

1. 把IRT表中这个请求的记录的ReplyRec字段加1

2. 把这个消息拷贝到等待队列(waiting queue)中去,Priority 设为1,其他MQMD域不变。

3. 如果ReplyRec = ReplyExp并且PropsOut = 0,设置MsgComplete标志。

未知或意外消息

一般情况下,本应用不会接到其他消息,但可能会有系统广播消息,或者返回应答消息其CorrelIds不对。CAM会把这样的消息放到CSQ4SAMP.DEAD.QUEUE队列,供以后分析,如果MQPUT这种消息时发生错误,会把这消息抛弃,程序继续进行。

2.2.2.4 发送应答

当CAM接到了它所需要的所有应答消息,它把这些结果综合起来,向前台发送一条查询结果。它把具有相同CorrelId的消息里面的数据合在一起,作为返回,把这条返回放到最初的贷款检查申请程序指定的reply-to队列里去。这个PUT动作和GET最后一个需要的返回信息是放在一个工作单元(UOW)里的,凡是完成了的查询,它的消息会被从等待队列CSQ4SAMP.B2.WAITING.n里清除,这样等待队列里里不会积累结束了的消息, 可以加快恢复(recovery)操作。

2.2.2.5 恢复部分完成的查询

CAM把它接收到的所有消息都拷贝到CSQ4SAMP.B2.WAITING.n,拷贝时把消息的MQMD进行如下设置:

  • Priority 根据消息类型设置:
    • 查询请求消息, priority = 3
    • 数据报文(datagram)消息, priority = 2
    • 应答消息, priority = 1
  • CorrelId 设为贷款审查请求消息的MsgId 。
  • 其它MQMD 域与接收到的消息保持一致。

当一个查询结束返回前台时,处理程序会把这个查询的所有消息都清除掉。这样,等待队列里存放的都是部分完成的请求,这些消息会用来在应用重新启动时进行未完成请求的状态恢复。这些消息设置成不同的优先级别,所以进行恢复时,查询请求消息会比传播报文和应答消息之前被读到。

2.2.3 帐户查询程序(CSQ4CVB3/ CSQ4CCB3)

帐户查询程序是被队列CSQ4SAMP.B3.MESSAGES上的触发器事件启动的。当它OPEN队列后,使用带等待参数的MQGET命令取数据,等待间隔设为30秒。

这个程序查询VSAM文件CSQ4BAQ,读取帐户信息包括这个帐户的户主名称、平均余额、信用等级等,并把这些信息返回过去。如果输入的数据在CSQ4BAQ中没有找到,返回说没有这个用户。

程序最后用MQPUT1命令把一 条应答消息放到贷款审查请求消息指定的reply-to队列里去。

PUT返回消息时如下设置:

  • CorrelId 字段拷贝请求消息的值。
  • 使用MQPMO_PASS_IDENTITY_CONTEXT选项

2.2.4 附加查询分发程序(CSQ4CVB4/ CSQ4CCB4)

附加查询分发程序是被CSQ4SAMP.B4.MESSAGES队列上的触发器事件启动的。为了模拟把贷款审查请求转发到其他象能够进行信用卡历史记录检查、储蓄帐户检查、历史贷款还款情况检查的程序,分发程序把原来的审查请求消息转发到若干个队列里去,这些队列的名字放在一个叫CSQ4SAMP.B4.NAMELIST的名字列表对象里。这个名字列表里放了三个队列名,分别叫做CSQ4SAMP.B5.MESSAGES、CSQ4SAMP.B6.MESSAGES和CSQ4SAMP.B7.MESSAGES。在实际的业务环境中,这些程序可能是在其他系统上,这样这个名字列表里定义的就应该是一些远程队列。如果你想把这个例子修改成这种模式,请参考"使用多个队列管理器的分布式Credit Check例子".

分发程序进行如下操作:

1. 程序首先从名字列表里取得要使用的队列名字,程序通过发送MQINQ命令查询名字列表对象CSQ4SAMP.B4.C.NAMELIST的属性来得到这些队列名。

2. 打开这些队列以及队列CSQ4SAMP.B4.MESSAGES.

3. 循环进行如下操作,直到队列CSQ4SAMP.B4.MESSAGES里没有任何消息

a. 使用MQGET命令读取输入,设置等待选项,等待30秒的间隔

b. 向名字列表里的所有队列PUT一条消息,指定恰当的队列名CSQ4SAMP.B2.REPLY.n做reply-to 队列。程序把贷款审查请求的CorrelId 放到这些拷贝的消息里,并且在PUT时使用 MQPMO_PASS_IDENTITY_CONTEXT选项。

c. 发送一个传播消息(propagation message)到队列CSQ4SAMP.B2.REPLY.n里,记录有消息被成功转发的个数。

d. 执行一个syncpoint.

2.2.5 代理查询程序(CSQ4CVB5/CSQ4CCB5)

附加查询处理程序模拟实际应用对贷款人的其它信用情况进行附加检查。代理查询程序有COBOL和C两个版本,他们的处理逻辑相同,可以互相替代。如果其它程序使用C语言,这个程序要用COBOL的,反过来也一样。这个例子可以充分体现通过MQ通信的程序可以使用不同语言共同工作,并且一个程序可以很方便地进行升级、替换。

代理查询程序也是被触发器时间启动的:

  • COBOL版程序(CSQ4CVB5)可以被以下队列触发:
    • CSQ4SAMP.B5.MESSAGES
    • CSQ4SAMP.B6.MESSAGES
    • CSQ4SAMP.B7.MESSAGES
  • C程序(CSQ4CCB5)会被CSQ4SAMP.B8.MESSAGES队列触发。

注意:

决定使用哪个版本的程序,可能需要对名字列表CSQ4SAMP.B4.NAMELIST进行修改 这个程序使用带等待参数的MQGET打开队列,设置等待时间30秒。它通过读取VSAM文件CSQ4BAQ来模拟对数据库的检索,然后把它使用的队列名加上客户的信用等级作为返回消息,为了简单些,信用等级是随机生成的。它通过MQPUT1命令返回结果,同时置MQMD:

  • 拷贝请求消息的 CorrelId
  • 使用MQPMO_PASS_IDENTITY_CONTEXT选项。

程序把返回消息放入请求消息指定的reply-to队列里去。 (目的队列的队列管理器名字也可以在请求消息里找到)

2.2.6 使用多个队列管理器的分布式Credit Check例子

你可以把这个Credit Check例子装在两个队列管理器和CICS系统中,他们合作完成交易。把代理查询程序放到一个CICS系统上,其他程序放在另一个系统上。你需要在两个系统上都配置好CICS adapter,然后做以下几步

1. 在两个队列管理器间建立好连接。

2. 对CSQ4SAMP.Bn.MESSAGES的n 是3, 5, 6, 或7着几个队列在一个队列管理器上定义为本地队列,另一面定义成远程队列。

3. 修改名字列表CSQ4SAMP.B4.NAMELIST的值,里面放那几个远程队列的名字。


回页首

3 设计思考

结合上面介绍的范例,下面开始讨论MQ应用程序设计时需要进行的一些考虑。主要有以下几个问题

  • 消息驱动的设计模式
  • MQ应用的进行交易一致性控制
  • MQ异步程序下保持数据一致性的设计
  • 消息持久性的选择
  • 消息上下文信息的传递
  • CorrelId和MsgId的运用
  • 处理错误
  • 处理意外的消息

3.1 消息驱动的设计模式

从这个例子当中看到,应用设计的模式与我们通常的CICS应用设计模式有很大不同,它是把整个应用划分成若干个功能模块,每个功能模块从一定的队列上接收输入,然后把结果输出到另一个队列;这样一来功能模块就可以看作等候在某个队列上的服务,一旦有消息来到,就根据它内在的业务逻辑完成一定的处理,而整个应用就是由若干个这样的服务联结而成的。应用程序可以是不断地监控队列,而更多的会是使用触发器,自动触发服务,依靠消息本身推动处理流程。这样的设计模式,把一个复杂的应用划分为若干较为独立的服务,由于MQ的支持,这些服务可以分布在任意地理位置的任意系统中,可以很方便地集成起来,而且这些单独的服务可以容易地实现在不同应用间的共享,能很大地加快新应用的开发速度。同时这也是把分布在不同系统上的现有应用进行整合,形成功能更强大的新应用系统的很好的方法。

3.2 MQ应用的进行交易一致性控制

MQ下的应用程序使用的是一种异步工作模式,这种模式下,应用中UOW的范围,和同步应用下是有很大不同的,应用设计中要充分考虑到这种区别:



上面所表示的是一个两个系统下的分布式应用下,同步程序和异步程序UOW范围的差异,在上部的同步模式下,所有操作都可以放在一个UOW中,通过两阶段提交协议实现数据一致;下部的异步模式,会分成3个UOW,第一个是应用程序在本地队列管理器中的操作,第二个是两个队列管理器间的数据传输,这个UOW是系统完成的,对于应用是透明的,第三个UOW是远程应用在远程队列管理器中的操作。应用设计时要充分意识到这些区别。

由于交易一致性控制,一个MQ应用中在队列中进行的改变,只在它COMMIT后,其他应用程序才能看到,所以在进行请求/应答模式的MQ应用程序中,请求程序发送请求消息后,为了应答程序能够得到它发送的消息进行处理,必须选择以下几种方法之一:

  • 请求程序中把MQPUT放在UOW之外。
  • 请求程序发送MQPUT后在适当的位置下COMMIT,然后在到应答队列里去等待返回。
  • 请求程序发送MQPUT后程序结束,在另一个程序或另一次运行中去取得返回结果。

范例不同地方根据实际情况使用了不同的方法。用户界面程序CSQ4CCB1中使用的是第一种方法,发送请求时设置MQPMO_NO_SYNCPOINT选项,这样MQPUT时将脱离交易一致性控制,一旦MQPUT正确返回,消息就放进了队列里,其他应用程序就能够进行读取。信用查询主程序CSQ4CCB2使用的是第二种办法;CSQ4CCB2读取输入后,要同时向等待队列(B2.WAITING.n)、帐户查询队列B2.OUTPUT.ALIAS(B3.MESSAGES)里,部分查询还要向B4.MESSAGES里各发送一条消息,这些发送放在一个UOW里,正确结束后先SYNCPOINT,然后再去查询返回。

应答程序也要与请求程序类似,也要合理地控制UOW的范围,使得返回消息能够恰当地被请求程序得到。

Credit Check例子还使用交易一致性控制来实现以下要求:

  • 对于一个审查请求,只会返回一个应答结果。
  • 意外消息不会重复放到死信队列里去。
  • CAM程序能够从持久性的等待队列里恢复部分完成的查询。

程序通过把一定的消息读取、处理和发送过程放到一个工作单元(UOW)里来实现以上目的。

3.3 MQ异步程序下保持数据一致性的设计

上面看到,在使用MQ进行要求同步通讯的程序设计时,会碰到原来可能会做单一UOW的应用,在MQ下的异步应用设计下要划分成若干个UOW,这就涉及到如何在多UOW下保证数据整体的一致性。这种需求,一般可以通过合理的冲正设计来实现,在Credit Check的例子中,由于没有UPDATA操作,所以没有做冲正逻辑的设计,但是它通过等待队列的逻辑来保证在程序重起时能够得到当前尚未完全完成的查询的状态,并正常继续下去。

在MQ下实现同步工作模式,就要灵活地使用MQ提供的消息生命周期功能和应用的冲正逻辑进行配合。在典型的情况下:

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

在系统B上,应答程序处理完请求,修改数据库后,返回结果消息,下SYNCPOINT,结果消息也设置生命周期T2(T2

在这种设计下,如果系统A发送后,交易没有被B读走就超时了,A正确显示"交易失败,超时",这条请求会被系统抛弃;如果B已经读取并完成了数据更改,由于意外原因B的应答消息没有被A读到,A显示"交易失败,超时"后将不会再读这条应答消息,它必将过期,过期后被系统复制到冲正队列,触发自动冲正,恢复了数据,整个的效果也是交易失败,数据没有改动。

通过这样的设计,可以看到数据一致性能够很好地保持。

3.4 消息持久性的选择

凡是设置了持久性的消息,系统都会把它存储进磁盘中,并且操作这些消息时都进行LOG操作,这样,在队列管理器正常或发生故障重起时,队列里的消息能够完整地保留,而没有设置持久性的消息将会丢失。消息的持久性可以在MQPUT时进行指定,也可以在队列上设置默认属性,MQPUT时的属性有较高的优先性。持久性消息保证了消息在系统和网络等故障下的安全可靠,但是同时从性能角度来讲会比非持久性消息要差,因此,要从应用的的角度进行权衡和分析,然后决定是否使用消息的持久性属性。当对性能要求非常高,可靠性要求相对不高时,可以首先考虑采用非持久性消息,在消息决不允许有任何丢失,并且在丢失之后又无法重新发送时,要使用持久性消息。在本范例中,由于联机查询要求较快地返回,时间一长即使返回,也没有用处,所以这些消息使用的非持久消息,而对于批量查询,从提交到查看结果,会有较长的时间,这期间不希望由于队列管理器重起丢失数据,所以这些消息使用的持久性消息。等待队列里的消息由于需要用来恢复未完成的查询,所以也设计成持久性消息。

消息的持久性和非持久性是消息本身的属性,一般情况下消息的发送者应在应用程序中显式地指定消息的持久性,而不要使用队列的默认属性,否则当队列的持久性属性(DEPSIST)发生变化时,设计时做的持久性选择就会有问题,本例中就是在MQPUT时使用MQPER_NOT_PERSISTENT或MQPER_PERSISTENT进行显示的指定。

3.5 消息上下文信息的传递

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) 最好传递从原始消息得到的上下文信息,这样在消息处理中的任意环节,都能够了解到最初发动者的用户信息。

当用户界面程序CSQ4CVB1/ CSQ4CCB1发送消息时,它使用了MQPMO_DEFAULT_CONTEXT选项。着意味着队列管理器会生自动成身份鉴别上下文(Identity context)和发送者上下文(Origin context)信息,这些信息部分是队列管理器从CICS中得到的,如启动程序的交易号 (MVB1) 、启动交易的用户ID等。后面的所有处理程序,MQGET消息时得到请求消息的MQMD,在它MQPUT时,都设置MQPMO_PASS_IDENTITY_CONTEXT选项,这意味着身份鉴别上下文(Identity context)可以从原来的贷款审核请求消息里复制过来,而发送者上下文(Origin context)会由队列管理器根据CAM程序的情况自动生成,这样一个消息的处理流程里生成的所有消息,都继承了发动请求的用户的身份鉴别上下文。

当CAM写返回消息时,它使用了MQPMO_ALTERNATE_USER_AUTHORITY选项,这会使队列管理器执行这一操作时使用另一个用户身份进行权限检查,而不是使用运行程序的用户,CAM这时把最初发动查询的用户ID放在这里,这样一个银行员工只能看到他发动的那些查询的返回结果。最初发动查询的用户ID就是从一直传递下来的身份鉴别上下文(Identity context)中得到的。

3.6 CorrelId和MsgId的运用

应用程序要对审查请求进行监控,看它需要的请求是否都返回了,它需要一种方法确定哪些返回信息是针对哪条请求的,CAM的做法是把原始请求的MsgId 字段拷贝到它所发出的请求消息的CorrelId 字段其他所有程序把接收到数据的CorrelId 拷贝到它返回数据的CorrelId ,以此来追踪一个请求相关的所有消息。

3.7 处理错误

用户界面程序只要简单地把错误报告给用户就好了,但其他程序却没有这个方便,所以他们必须用其他方式处理错误。某些情况下程序可能甚至不知道发送请求的用户ID(比如MQGET发生错误的情况),常用的方法是把这些错误写到特定的错误记录队列中,或使用CICS的机制记录错误。范例程序使用的是把错误消息放到CICS中叫做CSQ4SAMP的TSQ里面,你可以用CEBR去进行检查,程序同时还向CICS CSML log里记错误消息。

3.8 如何处理意外的消息

当设计消息驱动的程序时,你必须决定如何处理收到的意料之外的消息。有两种基本的处理方法:

  • 应用程序停下来,直到意外消息得到了人工处理。这时应用程序要通知操作员,程序退出,并保证不会自动重新启动(比如停掉触发器)。这样所有处理都停下来,直到问题由人工解决并重新启动应用。
  • 应用把消息从触发它的队列里移到别的地方,然后继续处理,通常的做法是把意外消息放到死信队列里去。

如果你选择第二种方法一般:

  • 要有操作员或另外一个程序去检查死信队列,看这些消息都是怎么回事,需要如何处理。
  • 如果向死信队列里写意外消息时出错,这个意外消息将被抛弃。
  • 如果意外消息很长,超过了程序缓冲区长度或死信队列允许的最大长度,消息将被截断。

为了使意外情况对应用系统影响最小,不会造成服务终止,范例选择了第二种方法。为了和其他应用的消息区分开来,Credit Check例子没有使用系统的死信队列,而是定义了自己的死信队列叫做CSQ4SAMP.DEAD.QUEUE


回页首

4.总结

通过本文对MQ for z/OS自带的一个的应用范例的分析,可以了解到如何进行消息驱动模式的应用系统结构设计,如何用MQ设计混合有联机/批量交易的应用系统,如何将CICS和MQ联合起来完成更丰富的应用功能,使MQ为企业带来更大价值。


关于作者

温洪涛,IBM公司软件部售前工程师,2005年加入IBM公司软件部,从事IBM大型机WebSphere家族软件的产品的技术支持工作。在进入IBM之前,长期从事银行大型机应用相关的项目,有丰富的应用开发和系统管理经验。曾经负责或参与过建行北京分行移植新一代核心银行系统CCBS、农行北京分行生产系统优化、中行SBS390系统迁移压力测试与性能优化、工商银行清算中心新一代国际清算业务系统、深圳建行DCC集中数据迁移、农业银行北京分行客户经理考核系统、农业银行总行个人负债业务系统开发等项目。