内容


任务:消息

使用循环队列简化管理和调试

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 任务:消息

敬请期待该系列的后续内容。

此内容是该系列的一部分:任务:消息

敬请期待该系列的后续内容。

为何使用循环队列?

当生成消息的速度大于使用消息的速度时,队列技术的经典用例是缓存消息。这是一种优雅的系统,在队列满员之前,它都能很好地工作。当队列满员时,系统从异步行为切换到同步行为,这时,消息生成者生成消息的速度只能与消息使用者使用消息的速度一样快。在大多数用例中,这是理想的行为,并被视为一个调优问题:增加 MAXDEPTH 值,直到队列能吸收最高峰值负载。

但在一些用例中,队列总是接收新消息可能更有用,即便那意味着丢弃最旧的消息。这种用例的经典示例是事件消息。我总是建议我的客户启用授权事件,以便轻松诊断任何授权错误。很多人还喜欢启用其他事件。如果没有监控代理实时使用这些事件,它们将在事件队列中堆积起来。一旦事件队列满员,那个队列的新消息将被丢弃。这将导致一种第 22 条军规 情况:如果您等到需要事件时才启用它们,那么需要诊断的问题已经发生,您错过了捕获事件消息的机会;但如果因为预期可能发生问题而启用事件,则当问题发生时,队列可能已经满员,您同样会失去捕获事件消息的机会。

本文演示如何配置一个队列,以便当队列快满员时,删除队列最前面的消息。这为到达的新消息预留出空间,以便队列在任何时候都拥有最新消息,而不是最旧的消息。由于这种行为类似于循环日志的行为,因此我将这个概念命名为 “循环队列”,经过几年的实践,我发现这种方法对于管理和调试非常有用。

概述

支持循环队列的配置包含以下几个组件:

  • 一个触发器监控器支持响应不断变化的队列深度。
  • 一个进程支持触发器监控器工作。
  • 本文附带的 ChompQ.pl 样例脚本包含用于确定要删除的消息数量的逻辑。
  • SupportPac MA01 的 q 程序用于删除消息。

我们将逐一检查这些组件。如果您不熟悉触发机制,没有关系!我将介绍设置并运行 ChompQ 示例所需的所有知识,并在 参考资料 部分提供一些关于触发机制的补充阅读资料的链接。

但在检查配置之前,我们先看看 ChompQ 脚本触发时将发生的事件:

  1. 队列深度超过 TRIGDEPTH 设置的阈值时,触发器触发。
  2. 触发器监控器截获触发器消息并检索队列的 PROCESS 属性指向的进程对象。
  3. 触发器监控器启动进程对象中指定的 ChompQ.pl,并将触发器消息作为一个命令行参数传递。
  4. ChompQ.pl 从触发器消息读取队列名称和队列管理器名称。
  5. ChompQ.pl 查询被触发的队列的 QDEPTHHI、CURDEPTH 和 MAXDEPTH,确定需要从队列删除多少消息。
  6. ChompQ.pl 调用 Q 程序删除消息,向其传送队列、队列管理器和要删除的消息数量。
  7. Q 程序删除消息并将控制权返回 ChompQ.pl。
  8. ChompQ.pl 在队列上重新启用触发机制并退出。
  9. 触发器监控器等待另一个触发消息。

每当队列深度超过 TRIGDEPTH 中配置的阈值时,这个事件序列都会重复。作为防止出现无限触发器循环的一种保护机制,ChompQ.pl 将总是至少删除一条消息。确切的行为和时间可以通过 QDEPTHHI、TRIGDEPTH 和 MAXDEPTH 的值调整。另外,可以使用同一个进程定义和触发器监控器在多个队列上配置 ChompQ,每个队列可以拥有不同的设置。

了解基本知识后,现在可以卷起袖子深入细节了。

先决条件

MQ 对象

首先,您需要一个队列管理器和一些队列。对于本文的目的,假定您拥有一个名为 PLUTO 的队列管理器(您也可以在您拥有的任何队列管理器上进行这个练习)。定义一个名为 CHOMPQ.TEST 的本地队列,并定义一个名为 CHOMPQ.INIT 的初始队列来服务它。这将确保您不会干扰可能已经在测试队列管理器上设置好的任何触发机制。下面定义它们:

清单 1
DEFINE QL(CHOMPQ.INIT) +
       DESCR('Initiation queue for testing ChompQ.pl') +
       REPLACE

DEFINE QL('CHOMPQ.TEST') +
       DESCR('Local queue for testing ChompQ.pl') +
       INITQ('CHOMPQ.INIT') +
       TRIGGER +
       TRIGTYPE(DEPTH) +
       TRIGDPTH(900) +
       MAXDEPTH(1000) +
       PROCESS('CHOMPQ') +
       QDEPTHHI(80) +
       REPLACE

您还需要定义一个进程对象,但确切的定义根据平台不同而不同,因此我们将稍后介绍。

SupportPac MA01

您需要下载 SupportPac MA01 — 如果没有的话。Q 程序是 WebSphere MQ 工具的 “瑞士军刀”,能够读写队列或主题上的消息并操作此 API 中可用的所有消息选项和属性。它包含针对各种平台编译的可执行文件,因此您只需解压缩适当的版本即可使用。我通常它 Q 程序放到 WebSphere MQ bin 目录中,您也可以将其放到队列管理器的 PATH 中的任何位置。如果您以前没有使用过 Q 程序,那么在继续这个练习之前,请您花点时间熟悉它。

Perl

Perl 是所有支持 WebSphere MQ 的 UNIX® 和 Linux® 发行版的一个标准组件。对于 Windows®,则可能需要首先获取并安装一个 Perl 副本。同样,对于 Windows 安装,首先应确保 Perl 可执行文件位于队列管理器的 PATH 中。要测试这一点,可以作为 WebSphere MQ 服务帐户登录,打开一个命令窗口,输入 perl –v。如果 Perl 已安装并位于 PATH 中,将显示一些版本信息。

死信队列

触发器监控器将被拒绝的触发器消息放置到死信队列(Dead Letter Queue,DLQ)上。但是,新建队列没有配置为使用死信队列。尽管存在一个名为 SYSTEM.DEAD.LETTER.QUEUE 的对象,但要启用死信队列,仍然需要使用死信队列的名称来更改队列管理器的 DEADQ 属性。检查要使用的队列管理器是否启用了 DLQ 处理功能。

触发机制简介

触发机制的目的是允许队列管理器基于消息的到达启动外部动作。一些队列属性决定何时出发触发器,一个进程定义包含用于响应触发器的命令行。一个名为触发器监控器的特殊程序接收来自队列管理器的触发器消息,读取进程定义,然后运行进程定义中指定的命令行。触发器监控器监听一个启动队列上的触发器消息。启动队列是普通本地队列。启动队列没有任何属性使其成为启动队列,启动队列的定义方式与其他本地队列一样。但是,被触发的队列拥有一个 INITQ 属性,该属性必须包含启动队列的名称,以便队列管理器生成触发器消息。

WebSphere MQ 提供支持触发机制的所有组件,但由管理员负责配置它们,包括启动触发器监控器。在生产中,触发器监控器通常作为一个队列管理器服务启动,它的输出将被重定向到一些日志文件。在测试阶段,将触发器监控器作为前台进程运行更方便,因为所有诊断消息将在命令会话窗口中可见。WebSphere MQ 提供的触发器监控器成为 runmqtrm,如果您作为管理帐户(通常是 mqm 或 MUSR_MQADMIN)登录,那么 runmqtrm 应该在您的路径中。要启动 runmqtrm,需要向它传递队列管理器和启动队列的名称:

runmqtrm -m PLUTO -q CHOMPQ.INIT

如果 PLUTO 队列管理器正在运行且启动队列已定义,那么您应该能看到一条消息,说明触发器监控器已启动,且正在等待触发器消息。

定义进程对象

进程对象告知触发器监控器将要执行的命令。确切的定义取决于您的服务器平台。本文下面的示例适用 Windows 和 UNIX,但如果您的安装目录不是默认目录,则需要更改它们。

清单 2
* Windows process definition for ChompQ:
DEFINE PROCESS(CHOMPQ) +
       APPLICID('perl C:\Progra~1\IBM\WEBSPH~1\exits\ChompQ.pl') +
       ENVRDATA('>>C:\Progra~1\IBM\WEBSPH~1\exits\ChompQ.log 2>&1 &') +
       REPLACE

* Unix process definition for ChompQ:
DEFINE PROCESS(CHOMPQ) +
       APPLICID('/var/mqm/exits/ChompQ.pl') +
       ENVRDATA('>>/var/mqm/exits/ChompQ.log 2>&1 &') +
       REPLACE

注意,上面显示的进程定义假定可执行程序(Windows 的 perl 和 UNIX 的 ChompQ.pl)位于队列管理器的 PATH 中。您可以提供完全限定路径,只要 APPLICID 字符串长度不超过 256 个字符。

还有一点需要指出,上面的进程定义旨在在后台启动 ChompQ.pl。这种方法支持同时运行几个 ChompQ.pl 实例,这是针对已触发程序定制的。如果移除 ENVRDATA 字段末尾的与字符(&),ChompQ.pl 将在前台启动,这样,触发器监控器必须等待 ChompQ.pl 完成才能读取下一条触发器消息。一种已触发程序测试策略是:在前台运行已触发程序(ENVRDATA 字段末尾没有 &),以便触发器监控器探测并报告任何问题。一旦程序运行正常,就恢复 ENVRDATA 字段末尾的与字符,以便触发器监控器能运行多个并发 ChompQ.pl 实例,从而更及时地处理繁忙的队列管理器上的消息量。鉴于我们的测试只有一个已触发队列,这些选项的表现几乎没有任何区别。仅当多个已触发队列使用同一个启动队列和触发器监控器时,这种区别才有关系。

ChompQ.pl 脚本

所有组件都已就绪,就差 ChompQ.pl 脚本了。我们将分别讨论这个脚本的每个部分。为节约您组合各个部分所需的复制粘贴时间,下载 部分提供了完整的代码清单。完整的代码包含 117 行,但只有 43 行是实际代码,其余内容都是注释。

如果您不关心这个 Perl 脚本的工作原理,只想安装它,请跳到下一节。

前导码

程序的第一部分引用几个 Perl 类并初始化几个变量:

#!/user/bin/perl –w

一个 Perl 程序的第一行通常是一个 shebang 字符(“shebang” 是 “shell” + “bang” 的缩写)。这一行告知 shell 应该使用哪个解释器来执行脚本。必须提供可执行文件的完全限定路径。还可以提供命令行参数,在本例中,Perl 警报功能被启用。

Windows 将忽略 shebang 行,但该行通常被保留,以便移植。在其他平台上,确保 shebang 行指向 Perl 可执行文件的实际位置,否则脚本将不能运行。在 UNIX 或 Linux 上,可以输入 which perl 确定正确路径。

清单 3
use strict;
use constant VERSION  =>  3;
use constant TESTING  =>  1; 
	
TESTING && print "=" x 60, "\n$0: ", scalar(localtime), "\n";

建议总是通过 use strict 运行(见清单 3)。TESTING 常量用于启用调试。将这个常量设置为非零值,以便显示诊断消息;或者将其设置为零,以便抑制诊断消息。上述代码最后一行展示如何将这个常量用作整个代码中的一个开关。从现在开始,我们将省略纯诊断性代码行,以便聚焦于直接执行程序功能的那些代码行。

清单 4
my ($Cmd, 
    $Resp, 
    %TrigMsg, 
    );	

# Program to delete messages.  Code expects q from MA01 SupportPac
my $Prog  = 'q';

下面,您为要执行的命令设置一些全局变量,为响应消息设置一个缓冲区,并设置一个哈希来包含来自触发器消息的字段(见清单 4)。$Prog 变量用于定位 Q 程序。如果它位于 PATH 中,则只需程序名称。否则,需要在这里放置完全限定程序名。

清单 5
# Trigger message field names & order
my @TrigFields = qw(StrucId Version QName ProcessName 
	TriggerData ApplType ApplId EnvData UserData QMgrName);

@TrigFields 数组按照原始顺序包含触发器消息字段的名称。稍后您将把这些名称用作 %TrigMsg 哈希的键。

不久前我修改了 ChompQ.pl,以便将其用于 Windows、UNIX 和 Linux 环境,这需要删除某些命令行中的引号。 要确定使用的是哪种操作系统风格,可以检查系统变量 PATH。下面的代码行搜索 PATH 变量,查找分号。如果找到分号,则系统假定为 Windows 且 $Quotes 被设置为一个空字符串;否则,$Quotes 被设置为双引号字符。

my $Quotes = $ENV{'PATH'} =~ /;/ ? '' : '"';

解析命令行参数

此时,所有必要变量都已设置好,可以开始程序的主要部分了。触发器监控器向已触发程序传递一条完整的 TMC2 消息(见清单 6)。这条消息是一个包含几个字段的结构,但对于已触发程序,它看起来是单个命令行参数。ChompQ.pl 的这个部分迭代命令行参数,查找 TMC2 结构。如果发现 TMC2,就将其解析为它的组成字段。

清单 6
foreach (@ARGV) {
    TESTING && print "Parm='$_'\n";
    if (/^TMC    2/) {
       (@TrigMsg{@TrigFields}) = unpack("a4 a4 a48 a48 a64 a4 a256 a128 a128 a48", $_);
        $TrigMsg{'$0'} = $0;  # Diagnostic
        foreach my $Key (sort keys %TrigMsg) {
            $TrigMsg{$Key} =~ s/\s+$//;  # Delete trailing blanks
            TESTING && printf "%11.11s = %s\n\n", $Key, defined $TrigMsg{$Key} ? 
		$TrigMsg{$Key} : "NULL";
        }
    }
}

外层 foreach 循环迭代 @ARGV 数组中的所有命令行参数,查找触发器消息。循环根据 StrucID 的存在性和参数开始处的 Version 字段探测消息。那是接近代码块顶部的正则表达式中的 “TMC 2”。由于 TMC2 字段的长度固定,unpack 命令用于将消息结构解析为一个数组。通过一点 Perl 技巧,字段名数组和已解析字段数组被合并到一个哈希中。然后,添加一个特殊哈希条目 $0,该条目包含 ChompQ.pl 的名称和版本,用于调试目的。

内部 foreach 循环迭代哈希条目,删除值最后的后缀空格字符。WebSphere MQ 总是使用后缀空格填充值,但这些值在作为命令传递到操作系统时会导致问题。如果 TESTING 启用,当循环执行时,哈希条目将以排序顺序显示。

要继续进行,程序必须已经接收一条有效的触发器消息,且该消息必须包含队列管理器的名称和被触发的队列的名称。如果这些值没有出现,程序将退出并显示一个非零返回码(见清单 7)。

清单 7
# Non-zero exit if we were triggered with something other than TMC2
exit 1 unless (exists $TrigMsg{'QName'} && $TrigMsg{'QMgrName'});

查询队列属性

知道队列管理器和队列名称后,就可以检索队列属性了。尽管可以使用 Perl MQSeries 模块以编程方式检索队列属性,但我希望解决方案所需的依赖项尽可能少。由于只要队列管理器存在,runmqsc 就必须可用,因此 ChompQ 通过以下方式与队列管理器交互:构建适当的 runmqsc 命令,执行命令,然后解析输出(见清单 8)。

清单 8
my @QAttrs = qw(CURDEPTH MAXDEPTH QDEPTHHI);
$Cmd = sprintf("echo %sdis ql('%s') %s%s | runmqsc %s 2>&1", $Quotes, $TrigMsg{QName}, 
join(' ', @QAttrs), $Quotes, $TrigMsg{QMgrName});

在本例中,一个数组用于容纳相关队列属性。这里,您查询的是当前和最大队列深度、最大消息长度、以及 QDEPTHHI 的值。sprintf 函数包含一个 runmqsc 命令模板,上述值将替换模板中的值。最有趣的值是 $Quotes。如前所述,在 Windows 中,带有圆括号的 echo 命令不需要引号,但在各种 UNIX 版本上则需要引号。由于 $Quotes 要么包含一个空字符串,要么包含一个双引号字符,因此可以将同一个模板用于这两种操作系统。这个命令如清单 9 所示。

清单 9
echo "dis ql('CHOMPQ.TEST') CURDEPTH MAXDEPTH MAXMSGL QDEPTHHI" | runmqsc PLUTO 2>&1

命令末尾的 2>&1 告知操作系统将 STDERR 作为 STDOUT 发送到同一个文件句柄。以这种方法合并 STDOUT 和 STDERR,将阻止 STDERR 输出在系统控制台上显示并允许程序解析它。

清单 10
$Resp = `$Cmd`;
foreach (@QAttrs) {$Resp =~ /$_\((\d+)\)/ && do {$TrigMsg{$_} = $1};}

然后,命令执行,$Resp 变量捕获输出(见清单 10)。然后,针对 @QAttrs 数组中的每个队列属性搜索 $Resp 中的文本。正则表达式捕获每个属性的值并将其放置到此前用于存放来自 TMC2 消息的字段的哈希中。

删除消息

现在,您拥有了计算要删除的消息的数量和调用 Q 程序来执行实际删除所需的所有代码。要删除的消息的数量计算如下:

Current depth – ( (Max Depth * QDEPTHHI) / 100)

队列管理器将 QDEPTHHI 中的值解释为一个百分比,只允许 0 到 100 之间的值。这能够满足您的要求,因为百分比向您提供足够的细粒度水平。例如,如果 MAXDEPTH 默认值为 5,000,QDEPTHHI 值 80 表示 4,000 消息,高于或低于该值一个百分点表示一个 50 条消息增量。

清单 11
my $DeleteCount = $TrigMsg{'CURDEPTH'} - int( $TrigMsg{'MAXDEPTH'} * 
	$TrigMsg{'QDEPTHHI'} / 100) ; 
$DeleteCount = 1 if $DeleteCount < 1;
	
$Cmd = "$Prog -dn -q -I$TrigMsg{'QName'} -L$DeleteCount -m$TrigMsg{'QMgrName'} 
	-=0  2>&1";
$Resp = `$Cmd`;

清单 11 中第一个代码行根据上面的公式计算要删除的消息数量。但是,有几种情况计算结果可能小于 1。其中,主要情况是 TRIGDEPTH 和 QDEPTHHI 没有针对对方正确设置。例如,如果 QDEPTHHI 设置为 80% 且 MAXDEPTH 为 5,000,ChompQ.pl 将试图将队列消息删减到 4,000 条。如果 TRIGDEPTH 设置为 3,000,那么 ChompQ 计算的需要删除的消息数量将是一个负数。如果 ChompQ.pl 最终没有采取任何操作,一个新触发器将立即触发,导致一个循环。为防止出现这种循环,需要一行代码来确保 $DeleteCount 总是大于或等于 1。然后,使用刚才计算的值执行 Q 程序,并由 $Resp 变量捕获输出。

在队列上重新启用触发机制

最后一步很关键,因为当一个队列在深度上被触发时,WebSphere MQ 将在触发器触发后仅用触发机制。已触发程序负责在退出前在队列上重新启用触发机制。

清单 12
$Cmd = "echo " . $Quotes . "alter ql('" . $TrigMsg{'QName'} . "') trigger" . $Quotes . 
	" | runmqsc " . $TrigMsg{QMgrName} . ' 2>&1';
$Resp = `$Cmd`;

一个 ALTER 命令通过前面描述的技术传递给 runmqsc。由于命令行拥有的参数少得多,代码只需串连所有必要值,而不是像上次那样使用 sprintf。命令行按照如下方式构造:

echo "alter ql('CHOMPQ.TEST') trigger" | runmqsc PLUTO 2>&1

同样,注意在 Windows 平台上,构造好的命令中将缺少双引号。一旦构造好,命令行将被执行,响应将被捕获到 $Resp 变量。

干净地退出

最后需要做的事情是使用一个零返回码退出,以便触发器监控器提交它在触发器消息上执行的 GET 并等待另一条消息:

exit 0; # Exit with Zero return code for Trigger Monitor

安装并测试 ChompQ

  1. 下载 ChompQ.pl 脚本。要安装它,只需将此程序转移到 WebSphere MQ exits 目录:
    • 在 Windows 上,这个目录默认为 C:\Program Files\IBM\WebSphere MQ\exits。
    • 在 UNIX 或 Linux 上,这个位置是 /var/mqm/exits。确保以文本模式转移文件,以便行结尾被转换。另外,还要运行命令 chmod 755 ChompQ.pl,使脚本可执行。
  2. 运行程序,验证其是否正确安装。首先转到 exits 目录。在 Windows 服务器上,如果 .pl 文件扩展已经关联到 Perl 可执行文件,则此脚本应该可以直接执行。如果是这样,只需输入 ChompQ.pl 启动程序。在 UNIX 和 Linux 服务器上,尝试使用 ./ChompQ.pl 替代。

    如果程序可执行,您应该会看到一个错误,告知您需要在命令行上传递一条 TMC2 消息。

  3. 最后,需要验证程序和配置是否匹配。为此,可以运行一个触发器监控器并将消息转储到队列中。触发器监控器将生成自己的诊断消息,查看这些消息的最好方法是在一个命令行窗口或终端会话中将触发器监控器作为前台进程运行。(下面的示例使用一个 Linux 服务器,但其中的命令也使用 Windows。)

    首先,打开两个命令窗口:一个用于运行触发器监控器并监视其输出,另一个用于执行命令并运行我们的测试。在第一个窗口中,使用以下命令启动触发器监控器:

    runmqtrm -m PLUTO -q CHOMPQ.INIT

    触发器监控器将启动并显示一些诊断信息(见清单 13)。

    清单 13
    mqm@linux-wmq-ams:~/Desktop> runmqtrm -m PLUTO -q CHOMPQ.INIT
    5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
    02/07/2011  09:30:37 PM : WebSphere MQ trigger monitor started.
    	
    __________________________________________________
    02/07/2011  09:30:38 PM : Waiting for a trigger message
  4. 下面,使用 Q 程序在队列上放置 950 条消息。如前所述,队列被设置为在达到 900 条消息时触发。MAXDEPTH 为 1,000,QDEPTHHI 为 80,结果得到 800 条消息(1000 * 80% = 800)的修剪后深度。当触发器在第 900 条消息处触发时,将导致一个比赛情况(race condition)。由于 Q 非常快,在 ChompQ.pl 启动之前,950 条消息将全部放置到队列上。这意味着 ChompQ.pl 将基于消息总数 950 计算修剪计数,您可以预期修剪后的队列深度为 800(见清单 14)。
    清单 14
    mqm@linux-wmq-ams:~> q -mPLUTO -oCHOMPQ.TEST -M'#!950 Msg'
    MQSeries Q Program by Paul Clarke [ V5.0.0 Build:Jul 17 2008 ]
    Connecting ...connected to 'PLUTO'.
    mqm@linux-wmq-ams:~>

    清单 14 展示使用 Q 将消息放置到队列上及其响应。

    清单 15
    mqm@linux-wmq-ams:~> echo 'dis ql(CHOMPQ.TEST) curdepth' | runmqsc PLUTO
    5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
    Starting MQSC for queue manager PLUTO.
    	
         1 : dis ql(CHOMPQ.TEST) curdepth
    AMQ8409: Display Queue details.
       QUEUE(CHOMPQ.TEST)                      TYPE(QLOCAL)
       CURDEPTH(800)
    One MQSC command read.
    No commands have a syntax error.
    All valid MQSC commands were processed.
    mqm@linux-wmq-ams:~>

    但队列深度显示时,它的确正好包含 800 条消息。我在前面提到触发器触发时会出现一个比赛情况。当队列为空时,您在其中放置了 950 条消息。现在队列拥有深度值 800,如果您现在运行相同的命令,队列填充的速度将比 ChompQ.pl 启动的速度快得多,Q 将收到一个 QFull 错误(见清单 16)。

    清单 16
    mqm@linux-wmq-ams:~> q -mPLUTO -oCHOMPQ.TEST -M'#!950 Msg'
    MQSeries Q Program by Paul Clarke [ V5.0.0 Build:Jul 17 2008 ]
    Connecting ...connected to 'PLUTO'.
    MQPUT on object 'CHOMPQ.TEST' returned 2053 Queue full..
    mqm@linux-wmq-ams:~>

    这表明您需要考虑消息到达针对 ChompQ 配置的队列的速度有多快。通常,事件队列逐渐聚集消息,因此,您可以将 TRIGDEPTH 和 QDEPTHHI 调优为接近 MAXDEPTH。另一方面,如果消息达到速度很快,则可能需要提高 MAXDEPTH 的值并大幅减小 TRIGDEPTH 和 QDEPTHHI 的值。TRIGDEPTH 和 MAXDEPTH 之间的差异应该达到足以容纳 1-2 秒内到达的消息。也就是说,如果 Q 程序生成消息的速度非常快,那么 ChompQ.pl 很可能跟不上。如果您遇到这种情况,那么 ChompQ 可能不是合适的解决方案。

故障诊断

ChompQ.log 文件

本文提供的代码已将 TESTING 常量设置为 1,这意味着每次调用 ChompQ.pl 都会显示诊断消息。如前所述,进程对象定义包含用于执行 ChompQ.pl 的命令行,输出被重定向到 WebSphere MQ exits 目录中的 ChompQ.log。这个日志文件现在应该包含几条来自您的测试的条目。下面,我们来检查一个典型的日志条目(见清单 17)。

清单 17
============================================================
/var/mqm/exits/ChompQ.pl: Tue Feb  8 21:23:51 2011

如清单 17 所示,每个条目都首先显示一个引人注目的行和一个时间戳。

清单 18
ARGV = 1.  Values = TMC    2CHOMPQ.TEST
CHOMPQ
/var/mqm/exits/ChompQ.pl >>/var/mqm/exits/ChompQ.log
2>&1
PLUTO
Parm='TMC    2CHOMPQ.TEST
CHOMPQ
/var/mqm/exits/ChompQ.pl >>/var/mqm/exits/ChompQ.log
2>&1
PLUTO                              '

下一行显示传入的参数及参数值。可以看到,触发器消息作为单个值传递,该值包含几个可识别的字段,比如程序、队列管理器、队列名称以及命令行。程序预期传入多个参数的可能性,并在每个参数解析时显示它们。这就是触发器消息显示两次的原因(见清单 18)。

清单 19
         $0 = ChompQ.pl v3
     ApplId = /var/mqm/exits/ChompQ.pl >>/var/mqm/exits/ChompQ.log
   ApplType =
    EnvData =  2>&1
ProcessName = CHOMPQ
   QMgrName = PLUTO
      QName = CHOMPQ.TEST
    StrucId = TMC
TriggerData =
   UserData =
    Version =    2

如果发现一条触发器消息,ChompQ.pl 将解析所有字段。如果启用了调试,将排序并显示字段。这有助于验证进程定义是否正确创建。如果 ChompQ.pl 运行但没有起作用,请检查这里,查看正在传送的是什么值(见清单 19)。

清单 20
MQSC Command = echo "dis ql('CHOMPQ.TEST') CURDEPTH MAXDEPTH QDEPTHHI" | runmqsc 
	PLUTO 2>&1
5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
Starting MQSC for queue manager PLUTO.
	

     1 : dis ql('CHOMPQ.TEST') CURDEPTH MAXDEPTH QDEPTHHI
AMQ8409: Display Queue details.
   QUEUE(CHOMPQ.TEST)                      TYPE(QLOCAL)
   CURDEPTH(950)                           MAXDEPTH(1000)
   QDEPTHHI(80)
One MQSC command read.
No commands have a syntax error.
All valid MQSC commands were processed.


DELETE = q -dn -q -ICHOMPQ.TEST -L150 -mPLUTO -=0  2>&1
RESPONSE = MQSeries Q Program by Paul Clarke [ V5.0.0 Build:Jul 17 2008 ]
Connecting ...connected to 'PLUTO'.

下面,您查看 ChompQ.pl 在哪里查询队列属性并计算需要删除的消息的数量(见清单 20)。-L150 命令行参数表示要删除 150 条消息。对于其他参数,请参阅 SupportPac MA01 中的 README 文件。

清单 21
5724-H72 (C) Copyright IBM Corp. 1994, 2009.  ALL RIGHTS RESERVED.
Starting MQSC for queue manager PLUTO.
	
     1 : alter ql('CHOMPQ.TEST') trigger
AMQ8008: WebSphere MQ queue changed.
One MQSC command read.
No commands have a syntax error.
All valid MQSC commands were processed.

mqm@linux-wmq-ams:~/exits>

最后,日志文件显示 ChompQ.pl 在队列上成功启用了触发机制。如前所述,当触发器触发后,队列管理器基于队列深度禁用触发机制。如果 ChompQ.pl 不在队列上重新启用触发机制,触发器将只被触发一次。

如果 ChompQ.pl 不运行

ChompQ.pl 不运行的原因有很多。在 UNIX 或 Linux 上,主要原因是程序以二进制模式传输。这将保留 Windows 式换行符,而 Perl 解释器不喜欢它们。另一个常见原因是脚本没有标记为可执行,导致一个权限拒绝错误返回触发器监控器。错误的进程定义也可能导致相同的症状。这些错误的结果是触发器监控器将触发,但值显示一个如下所示的类似错误:

Error '32256' starting triggered application (errno 0)

由于触发器监控器能服务多个队列,因此它需要具有一定弹性,继续运行,不管这个错误。ChompQ.pl 不工作时最明显的问题标志是队列总是设置为 NOTRIGGER。如果您在队列上启用触发机制,一个触发器将立即触发,然后,队列再次被重置为 NOTRIGGER。如果出现这种情况,验证进程定义是否正确,然后确保从命令行执行 ChompQ.pl。

磁盘空闲空间缓慢消失

禁用调试时,ChompQ.pl 将生成最少的日志条目;反之,将生成冗长的日志条目。如果您发现包含 exits 目录的磁盘分区上的空闲空间逐渐减少,则应验证是否已经禁用调试。要禁用调试,将 TESTING 常量设置为零:

use constant TESTING => 0;

这将抑制脚本生成的几乎所有诊断消息。

后安装任务

这个代码正常工作后,还有两个任务需要完成。第一个任务是确保诊断消息受到抑制。编辑 TESTING 常量,将其设置为 0 而不是 1。

第二个任务是设置触发器监控器,以便它作为一个服务运行。这将确保触发器监控器在队列管理器启动时启动,在队列管理器关闭时停止。使用清单 22 显示的命令定义服务。

清单 22
DEFINE SERVICE(CHOMPQTRM) +
       CONTROL(QMGR) +
       SERVTYPE(SERVER) +
       STARTCMD('+MQ_INSTALL_PATH+bin/runmqtrm') +
       STARTARG('-m +QMNAME+ -q CHOMPQ.INIT') +
       STOPCMD('+MQ_INSTALL_PATH+bin/amqsstop') +
       STOPARG('-m +QMNAME+ -p +MQ_SERVER_PID+')

注意,对象定义包含几个替代参数,比如 +QMNAME+。这是故意为之,不要更改它们,队列管理器将在运行时填充正确的值。这允许同一个定义跨平台工作。

结束语

永不满员的循环队列概念有很多用处。我已经提到了事件队列,除此之外,还有以下几种用处:

  • 订阅监控。发布/订阅应用程序开发过程中,可以创建一个管理订阅,将消息转储到一个队列中以便检查,这样做通常很方便。同样,如果订阅队列满员,就会很不方便。
  • 异常队列。在非生产环境中,异常队列通常在满员时被清空,以免应用程序停止。在这里应用循环队列有两个好处:一是消除手动管理队列的需求和停用;二是在队列中保留足够的消息,以便调试。
  • 受控的可持续订阅。受控的可持续订阅队列(managed durable subscription queue)的默认 MAXDEPTH 是 999,999,999。如果订阅者应用程序无法使用消息,消息可能会累积起来耗尽所有可用磁盘空间。在非生产环境中,在模型订阅队列上配置 ChompQ 可能比较方便。这样,不受监控的受控订阅将受到一个合理的 MAXDEPTH 限制,直到它们被识别并删除。

注意,上述所有用例都提到了非生产环境。在生产环境中定期丢弃消息并不是个好主意。替代方法是使用一个监控代理在队列深度太大或磁盘空间开始减少时发出警报。

尽管如此,循环队列仍然可以简化开发人员、测试人员和管理员在生产环境之前的工作。您可以随意试验循环队列,发明自己的方法来应用 ChompQ 概念。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=659766
ArticleTitle=任务:消息: 使用循环队列简化管理和调试
publish-date=05192011