最佳实践:WebSphere MQ 共享队列与应用程序

引言

有很多讨论 IBM® WebSphere® MQ 共享队列的资料,特别是讨论其提供消息持续可用性方面的资料更是数不胜数。但在规划和实现期间出现了很多关于如何最好地使用共享队列及其对应用程序的影响方面的问题。其中一些问题得到了广泛的关注(如确保数据序列化的应用程序代码等),而其他问题却没有得到足够的重视。本文所给出的建议均以生产环境中的实现为基础,通常包含可应用于所有 WebSphere MQ 应用程序的最佳实践。除非应用程序可靠且具备高可用性,否则在保证基础设施可靠且具备较高可用性方面所作的努力将效果甚微,因此,应用程序最佳实践对共享队列实现的成功也至关重要。

一些共享队列的主题并未在本文中涉及。WebSphere MQ V6 包含了一个新功能,允许共享队列中存在大于 63K 的消息,但由于在撰写本文时并未在任何生产环境中实现 WebSphere MQ V6,因此并不讨论此类消息对应用程序和资源利用方面的影响。此外,也不会讨论共享通道和组内队列。

示例应用程序

本文中所使用的示例应用程序名为 PTR。它所预期处理的消息量是每天 1,000,000 条消息(后文将讨论峰值速率),消息平均大小为 3.5K(含消息描述符——MQMD)。这些消息分布在两个队列中,两个队列中的消息量平均分配。这些消息将由一个名为 PTRC 的 CICS 事务进行处理。

问题 1:Coupling Facility 列表结构和队列大小

与私有队列不同(私有队列的大小受页集大小(根据 WebSphere MQ 不同,最大为 4G 或 64G)的限制),共享队列的大小受到应用程序列表结构大小的限制。此结构是 Coupling Facility (CF) 中的已分配存储区域。CF 是一个公共资源,由系统本身使用:DB2、CICS、IMS 和其他 WebSphere MQ 结构。该结构的大小是由 z/OS 系统管理员通过 IXCMIAPU 设置的。每个 WebSphere MQ 队列共享组 (QSG) 都至少需要使用两个结构,一个用于进行管理的结构,以及一个或多个应用程序结构。

在实际中,大部分队列共享组都定义为一个管理结构和至少两个应用程序结构,一个用于持久性信息,一个用于非持久性消息。WebSphere MQ 管理员需要知道持久性消息是否会进入共享队列。可以容纳持久性和非持久性消息的混合的队列必须在持久性列表结构中定义。

CF 所施加的大小限制使得一些客户询问这样的问题:它们的大容量应用程序是否适合使用共享队列。在某些情况下,CF 容量并不妨碍共享队列的使用,例如,在批处理作业中就是这样(此类作业中数百万消息长时间处于一个队列中)。但在大多数情况下,这个问题被估计过高了,而实际如何使用队列(例如,消息通常在队列中保持多长时间)对于确定使用共享队列是否恰当更为重要。

与 WebSphere MQ 一起提供的示例应用程序设置的初始大小为 266 MB,最大大小为 512 MB。这一部分将向您演示如何计算按照定义可以将多少 PTR 消息放入该结构中。

关于应用程序组的讨论应该可以帮助确定在峰值期间预期的最大深度、消息的大小以及队列上的预期持续时间。在知道消息处理速率和实际用户使用系统之前,进行这些评估可能有些困难。但和进行容量规划工作一样,您需要一个基准。

评估应用程序结构的需求情况

假定应用程序开发人员要求队列上应该有足够容纳每天预期消息量的 20% 的空间,以便能处理峰值期间大量消息涌入系统或较慢的事务处理无法跟上请求提交的速度时的情况。此行为属于例外,仅会在网络或应用程序停工期间出现。要求仅容纳 PTR 消息的 CF 结构大小应为:

  • 1M 消息的 20% = 200,000
  • 平均消息大小 = 3.5K = 3584
  • 消息数量 * 大小 = 200,000 * 3584 = 716,800,000
  • 加上 30% 的系统开销 = 931,840,000(请参阅下面的注释)
  • 所需的 1K 大小页面的数量 = 910,000 = 889 MB = 近 1GB

注:30% 的系统开销允许量是基于 Coupling Facility Control Code (CFCC) 第 12 级确定的。如果您的 CFCC 级别不同,此值可能会有变化。有关更多信息,请参阅 WebSphere MQ SupportPac MP16: Capacity Planning and Tuning for WebSphere MQ for z/OS

889 MB 的要求可能并不现实;事实上,这比 WebSphere MQ V5.3 所定义的缺省应用程序结构大小还要大。当与 z/OS 系统程序员进行讨论时,提出这个值可能会让他们觉得好笑。如果此值过高,那么预期日消息流量的 5% 如何呢?如果 1,000,000 MPD 为一个非常准确的速率,那么这个值就为一个多小时内的消息量。计算过程如下:

  • 1M 消息的 5% = 50,000
  • 平均消息大小 = 3.5K = 3584
  • 消息数量 * 大小 = 50,000 * 3584 = 179,200,000
  • 加上 30% 的系统开销 = 232,960,000
  • 所需的 1K 大小页面的数量 = 227,500 = 223 MB

此值也可能不太现实,因为其大小几乎占据了整个初始结构大小。如果 PTR 队列已填满,则在该结构中定义了队列的其他应用程序可能会受到影响。

1% 的最低容量要求可能是唯一可行的选项:

  • 1M 消息的 1% = 10,000
  • 平均消息大小 = 3.5K = 3584
  • 消息数量 * 大小 = 10,000 * 3584 = 35,840,000
  • 加上 30% 的系统开销 = 46,592,000
  • 所需的 1K 大小页面的数量 = 45,500 = 45 MB

定义共享队列

定义请求队列时,请将最大消息量设置为在确定结构的大小时使用的消息数量。z/OS 上的缺省队列深度 (MAXDEPTH) 为 999,999,999 条消息。不过可能在达到这个极限前出现结构/存储介质用完的情况,因此务必记住,除非受到队列深度的约束,一个队列上的消息可能形成堆积,从而用完整个应用程序结构。填满该结构将会影响在该结构中定义了队列的所有其他应用程序。

若要将请求队列定义为可容纳预期容量的 1%,相关定义将与以下所示内容类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
DEFINE QLOCAL('PRTA.REQUEST') CMDSCOPE(' ' )  QSGDISP(SHARED)
        REPLACE
        DEFPSIST(YES)
        BOQNAME('PRT.REQUEST.BOQ' )
        BOTHRESH(3)
        CFSTRUCT(QSG1PERS)
        HARDENBO
MAXDEPTH(5000)
QDEPTHHI(80)
QDEPTHLO(40)
QDPHIEV(ENABLED)
QDPLOEV(ENABLED)
QDPMAXEV(DISABLED)
DEFINE QLOCAL('PRTB.REQUEST') CMDSCOPE(' ' )  QSGDISP(SHARED)
        REPLACE
        DEFPSIST(YES)
        BOQNAME('PRT.REQUEST.BOQ' )
        BOTHRESH(3)
        CFSTRUCT(QSG1PERS)
        HARDENBO
MAXDEPTH(5000)
QDEPTHHI(80)
QDEPTHLO(40)
QDPHIEV(ENABLED)
QDPLOEV(ENABLED)
QDPMAXEV(DISABLED)

其他队列定义注意事项

在上面的示例中,所定义的队列最多将占用结构中 45 MB 大小的空间。如果需要在该结构中定义其他应用程序队列,请按照相同的大小确定方法进行操作。

向应用程序结构添加队列的另一个需要注意的方法是重叠的峰值期。例如,如果有两个需要 45MB 存储空间的队列,而结构只有 64MB,如果这两个队列的峰值处理期不重叠,或者如果队列深度永远不会达到最大值,则可能永远不会出现结构或队列满的情况。

问题 2:队列深度

即使消息量很大,共享队列仍然能够在大多数应用程序中正常工作。真正的限制因素不是有多少消息通过队列,而是队列通常的深度为多少——这个数字应该非常小。对于有大量消息的应用程序,服务器流程需要具备高可用性,而且能够快速从队列中提取消息。

当评估队列深度要求时,消息处理速率和峰值消息量同样重要。可以通过检查 System Management Facility (SMF) 队列核算记录 (SMF116) 来确定消息量和处理速率。如果这些记录不可用(很多客户并不会在生成环境中打开这个级别的报告功能),则可以基于统计数据记录 (SMF115)、该应用程序中的审核日志、消息驱动的数据库操作的平均数或其他应用程序指标来评估消息量和速率。

另一个评估工具是 RESET QSTATS 命令。如果在正常的生产或测试周期中按一定的间隔发出此命令,则可以捕获和跟踪在此期间已处理的消息数量和队列最高深度。

如果此应用程序为新应用程序或无法计算消息速率,则必须根据在测试期间产生的 SMF 数据来评估和推测消息量和处理速率。当进行此类评估工作时,请不要假定每天的消息都是平均分布的。很多请求类型在可预测的峰值期(如正常的业务时间内)都要多很多。每天可能会有多个峰值期,例如,在金融行业,在刚开市后和即将闭市前都有大的峰值期。其他峰值期可能并不是一天中的某个时段,而可能与月末、年末、放假前或其他不易预测的时段(如促销活动或故障转移)相关。

请与您的开发团队讨论预期的消息流量分布情况,接着在预生产测试期间测试和评估峰值处理速率,然后请确保在应用程序投入生产环境后对消息流量(特别是共享队列上的消息流量)进行监视。

下面我们举例说明每日峰值消息速率:如果我们假定预期的 1,000,000 条消息中的 75% 都是在八小时工作期间进入,则在此期间平均每秒达到请求队列的消息流量将为 26 条消息。如果峰值期更短,或者在峰值期进入的消息量的百分比更高,每秒的消息量自然会增加。为了保持队列深度值较低,服务器进程必须以更高的速率运行。

处理速率对队列深度的影响

在下面的示例中,消息的传入速率为提取速率的两倍,而您会发现度量深度迅速地增加。为了简单起见,我们使用 2:1 的速率比值——每个应用程序的情况不同,而且速率通常会随时间而发生变化。服务应用程序比请求应用程序慢的这一情况通常是由于服务器应用程序所进行的工作——读取表、进行计算、更新行等等——造成的。

在预生产测试中,应当确定传入和提取速率。虽然这些速率可能并不与生产环境中的情况相符,不过应该能以此为基础来确定,为了跟上请求应用程序速度,将必须运行多个服务应用程序的副本。

图 1
图 1

显然,如果为此应用程序指定了 0.5 秒的响应时服务水平协议,将很快就无法符合这个协议要求。如果队列深度设置为前面所计算的 5000,在半个小时内就会超个这个值。可以采用很多技术来对此情况进行处理,以下给出了一些较为常用的技术。

图 2
图 2

如果没有妨碍运行多个服务器应用程序副本的因素,则应对服务器应用程序处理消息的速度比请求应用程序慢的最简单解决方案就是运行多个服务器程序副本。在这个简单的示例中,只需要使用两个服务器应用程序副本即可。而在实际中,应用程序的多个副本通常不会以相同的速率运行,为了确定实际的速率,需要进行应用程序测试和评估。

在我们给出的简单示例中,由于服务器进程是一个 CICS 事务,直接在多个 CICS 区域中运行多个服务器事务副本就可能将队列深度保持在很低的水平。可以对 IMS 事务采用类似的技术。其他进程(批处理、应用服务器等等)要求使用更为复杂的方法,但几乎对于每种正在使用的进程模型,都有与共享队列一起使用的例子。

让足够的处理事务、足够的程序进行 MQGET 操作,可以在不违反 SLA 或超过容量的前提下对这个接收大量消息的队列进行共享。

问题 3:数据序列化

另一个常见的注意事项就是数据序列化。如果消息必须按照严格的序列进行处理,则只能有一个传入消息的应用程序和一个提取消息的应用程序,而这通常要求增加队列的大小,以容纳更多的消息。有关应用程序编码以保证序列化的更多信息,请参阅 IBM 红皮书 Parallel Sysplex Application Considerations (SG24-6523)

序列化要求及其影响并非 WebSphere MQ 处理所独有的。但通常会将其作为一个问题看待,特别在使用可以从多个位置初始化的实时进程替换基于文件的批处理进程时更是如此。从数据可用性的角度来看,应当尽可能消除序列化要求。如果不可能将其消除,或者不能在近期内消除,则可以使用简单的目标序列化技术来帮助管理序列化要求。

目标序列化

减少数据序列化要求影响的一个简单方法是确定其是否应用于数据的一个逻辑子集。例如,在很多情况下,仅要求在一个特定的帐户和项编号内进行数据序列化。如果是这样,请求应用程序可以将这些消息送入接收已定义范围内的序列化数据的队列中。每个请求队列将仅具有一个服务应用程序,从而确保序列化效果,但总体工作负载并不限制在一个服务器进程内。和上面的多服务器示例一样,队列堆积的情况也可以得以避免。

下面给出了一个目标序列化的示例:

图 3
图 3

这将要求应用程序打开多个队列,应用分发逻辑,等等。此外,随着消息量的增加,可能必须对应用程序进行更改,以打开更多的队列。

如果应用程序已投入生产环境,而不可能进行更改,或者,如果消息来自多个源,则可以使用 WebSphere Message Broker 或自定义应用程序来提供类似的功能。添加这一处理的的开销通常要比大容量序列化进程的影响小得多。下面给出了一个示例:

图 4
图 4

此模型的优势在于,随着消息量的增加而添加新队列时,并不要求对请求应用程序进行更改。另外,如果分发模式随着时间发生了变化,与在分布式应用程序中进行更改相比,改变规则驱动的引擎中用于分发消息的标准更为简单。

问题 4:消息关系

某些序列化要求不仅是由于消息必须按照顺序进行处理,而且还因为各个消息之间具有特定的关系。通常,消息之间是逻辑相关的(如多个消息组成一个应用程序事务时),或者基于类型进行排序的(如仅在初始命令被接受后才能处理相应的变更或取消操作)。

从应用程序编码的角度而言,要求所有消息或记录序列化要比根据消息间的关系进行编码要简单一些。这个要求可能是由于面向文件的批处理造成的,在此类处理中,确保信息按照预定义的序列进入进程中非常简单。

消息间的关系可能很紧密,也可能比较松散。当多个消息必须一起处理以完成一个逻辑事务时,此关系就非常紧密,如在购买订单中,订单通常由一个 Header 信息、一个或多个行式项目和定制信息组成。松散关系通常为修改请求,只有原始事务完成之后,才能处理这些修改请求。例如,仅在原始事务完成之后,才能修改订单或取消信用卡收费。

使用应用程序逻辑处理消息间的紧密关系

对于消息间的紧密关系,WebSphere MQ 提供了消息分组功能,以将多个消息以逻辑方式绑定为一个逻辑事务。从应用程序角度来看,传入消息的应用程序必须:

  1. 通过 MQMD GroupID 字段标识消息。可以通过从应用程序信息设置 GroupID 字段,或让 WebSphere MQ 生成 GroupID 来完成此操作。
  2. 让消息保持逻辑顺序 (MQPMO_LOGICAL_ORDER)。
  3. 标识每个消息在组内的逻辑位置。

提取消息的应用程序必须:

  1. 指定要按照逻辑顺序检索的消息 (MQGMO_LOGICAL_ORDER)。
  2. 在组内的所有消息为可用前不对消息进行处理 (MQGMO_ALL_MSGS_AVAILABLE)。

使用应用程序逻辑处理松散消息关系上顺序错乱的情况

在某些应用程序中,只有一小部分消息需要进行序列化。典型的例子是订单取消必须在初始订单之后达到。如果多个服务器从共享队列中提取消息,消息可能不会按顺序提取。应用程序设计人员可以决定包含特定逻辑,以处理这种顺序颠倒的情况,如下列伪代码示例所示:

1
2
3
4
5
6
IF a message cannot be processed because it is out of sequence
(e.g. an order change precedes an order create request:
THEN DO
    DELAY for a small time (typically a few milliseconds)
    ROLLBACK the transaction (increments MQMD.BackoutCount)
END

我们假定 MQGET 处理中已经包含了对 Backout Count 的检查(请参阅下面关于搁置的讨论)。DELAY 将允许等待被搁置的消息之前的消息达到。只有在很小一部分消息(通常为成对消息)需要排序时此技术才能起作用。

问题 5:空间不足和其他问题

即使是私有队列,也会不时被填满,这种情况通常出现在长时间存在网络故障时。一些客户发现,由于 CF 所施加的更为严格的大小限制,共享队列可能更容易出现填满的情况。传入消息的应用程序应对以下返回代码进行解释并采取相应操作:

1
2
3
4
5
MQRC_Q_FULL (2053, X'805') Queue already contains maximum number of messages
MQRC_STORAGE_MEDIUM_FULL (2192, X'890') External storage medium is full
    (also known as MQRC_PAGESET_FULL)
MQRC_CF_STRUC_FAILED (2373, X'945') Coupling-facility structure failed.
MQRC_CF_STRUC_IN_USE (2346, X'92A') Coupling-facility structure in use.

相关的操作取决于应用程序和环境。其中一些应用程序操作包括:

停止传入消息的应用程序
这通常是采用编程方式能够完成的最简单方法,不过却能有效地使应用程序变为不可用状态。
对共享队列采取抑制措施
WebSphere MQ 监视工具可能采取此操作,此类工具会在应用程序检索到相应的返回代码前检测到队列或介质已满的情况。
对传入消息的应用程序进行节流
通过加入一个循环或等待间隔减慢其传入消息的速度。此技术对批处理有效,不过要求对应用程序进行修改和测试。如果传入消息的应用程序是 Message Channel Agent 而您使用的是 WebSphere MQ V5.3.1,则可以通过接收器出口或在远程程序中完成节流工作。在 WebSphere MQ V6 中,已经向 z/OS 上的接收器通道添加了 retry 参数。这个参数可控制在确定消息无法送达其目标队列之前将进行重试的次数。
当共享队列填满时,将消息放入本地队列
虽然这意味着应用程序“知道”使用的是共享队列,但这仍然是一项不错的技术,可用于处理队列可能填满的任何情况。应用程序甚至可以将定义的搁置队列用于此目的。此选项要求一定的恢复功能,以便处理消息,或在共享队列变为可用时将其移到共享队列中。一种常见的技术是,对每个这样路由的消息应用 WebSphere MQ DLQ Header,以便在不必编写处理程序专门进行处理的情况下将消息移到共享队列中。

问题 6:消息搁置

在示例队列定义中包含了搁置信息,如下面突出显示的部分所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
DEFINE QLOCAL('PRT.REQUEST') CMDSCOPE(' ' )  QSGDISP(SHARED)
        REPLACE
        DEFPSIST(YES)
        BOQNAME('PRT.REQUEST.BOQ' )
        BOTHRESH(3)
        CFSTRUCT(MQM1DIVS)
        HARDENBO
        MAXDEPTH(10000)
        QDEPTHHI(80)
        QDEPTHLO(40)
        QDPHIEV(ENABLED)
        QDPLOEV(ENABLED)
        QDPMAXEV(DISABLED)

MQ 管理员设置各个搁置参数时,他们实际上是使用行为良好的 WebSphere MQ applications 来从队列中移除“中毒”的消息或由于其他问题而不能处理的消息。这对于共享队列和私有队列都非常重要,不过各自的症状可能有所不同。这些参数有:

BOQNAME('PRT.REQUEST.BOQ')
应用程序应将搁置的消息写入其中的队列的名称。
BOTHRESH (3)
每个消息处理尝试的次数。
HARDENBO
当同步点设置完成后将搁置计数器保存到磁盘。

如果私有队列上的消息不能获得处理,而发出了回滚命令,该消息将回到队列的顶部。该队列的下一个 MQGET 命令将拾取问题消息,并尝试再次处理它。如果尝试失败,事务将回滚,此消息再次回到队列的顶部。WebSphere MQ 维护搁置计数,该计数会在循环处理同一个消息时通知应用程序。如果应用程序忽略了搁置计数器,消息“中毒”的第一个特征就是队列深度开始增加而同时提取进程仍在继续运行。

在共享队列上,当有多个服务器进程在运行时,队列深度可能不会大幅度增加,因为在处理“中毒”的消息时,也在同时从队列中拾取和处理其他消息。仅有一个恶意消息,它会让提取进程一遍又一遍地进行处理,占用了大量的 CPU 时钟周期,不过却不会使工作完全停顿。

在某些情况下,将消息搁置并允许进行一定的处理尝试是合理的。例如,如果数据库行或表不可用,可能在下次处理该消息时就变得可用了。

行为良好的应用程序在每次读取消息时都会检查搁置计数,如果该计数不为零,则将其与队列上定义的搁置阈值进行比较。如果计数大于阈值,应将此消息写入 BOQNAME 参数中指定的队列并提交。通常会将一个 Dead Letter Header (MQDLH) 附加到消息上,以指示消息为何写入搁置队列。

建议

  1. 每个应用程序(而不是每个队列)使用一个搁置队列。可将一个搁置队列用于多个请求队列。
  2. 不要使用队列管理器定义的“死信”队列作为搁置队列,因为搁置队列的内容通常是由应用程序驱动的。只有万不得已才能将“死信”队列作为搁置队列。
  3. 不要将搁置队列作为共享队列使用,因为搁置队列通常以批处理方式进行处理,因此这些队列上的任何队列深度堆积都可能影响其他使用相同 CF 结构的应用程序。因此,如果将搁置队列定义为共享队列,需要可以承载服务器应用程序的 QSG 中的每个队列管理器上都有一个搁置队列的实例。
  4. 如果将一个 DLH 应用到了搁置消息,通过使用多个“死信”队列处理程序之一就可以确定是否应将消息还原到原始队列中。很多客户都使用标准处理程序,或对其进行定制,以满足应用程序的需求。

问题 7:共享和私有队列混合选项

一些应用程序对特定服务类型具有很高的可用性要求。首要客户或高经济价值的请求可能需要共享队列提供的持续可用性,而其他消息流量则不需要此级别的服务。和上面的“目标序列化”示例一样,可以使用应用程序或代理工具将选定的消息路由到共享队列,而将其他消息路由到隐私队列(可能是作为集群的一部分定义的)。

问题 8:其他高可用性选项

有时候共享队列并不能满足可用性需求。对于此类应用程序,WebSphere MQ 集群通常可提供比点到点实现更高的可用性级别。此外,还可以使用标准故障转移技术,以用于在原地或不同的 LPAR 上重启队列管理器。

致谢

正如我曾祖父常说的,“众人拾柴火焰高”。很多人提供了他们的经验、帮助改进文中的各种想法、检查我的数学计算部分、纠正打字错误以及帮助我纠正错误的语法。特别感谢 Emir Garza、Bob Herbison、Mark Taylor 和 Steve Zehner。

原文:https://www.ibm.com/developerworks/cn/websphere/library/techarticles/0512_elkins/0512_elkins.html

猜你喜欢

转载自blog.csdn.net/luoww1/article/details/80137703