任务:消息

使用一个 Windows 服务来启动 WebSphere MQ File Transfer Edition 客户端代理

Comments

系列内容:

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

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

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

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

简介

在我最早的 WebSphere MQ File Transfer Edition 部署中,我的客户端想在一些无人值守的 Windows 服务器上运行 WebSphere MQ File Transfer Edition 客户端代理。尽管 WebSphere MQ File Transfer Edition 现在不提供一个 Windows 服务了,但要使代理作为后台任务运行并不困难。真正的麻烦是如何使它们具有弹性。fteStartAgent 命令生成一个子进程然后终止。这意味着,如果配置为一个 Windows 服务,它要么不能重新启动,要么在每个间隔都尝试重新启动。我们可以绕过 fteStartAgent 代理调用底层类来直接启动这个代理,但必须小心翼翼,以免使用已建档的(documented)命令。

我们决定使用的解决方案是本文的主题。它包含两部分:作为 SupportPac MA7K 提供的 Windows Service Trigger Monitor 程序,和少量以 Perl 编写的 “粘合剂” 代码。它不仅按照理想的方式启动 WebSphere MQ File Transfer Edition 代理,实际上它还使这个代理非常难以终止。

核心组件是 SupportPac MA7K,它依赖 WebSphere MQ Client。WebSphere MQ Client 作为 SupportPac MQC7 可用(参见 参考资料 了解关于 SupportPacs 的信息)。MA7K 服务连接到一个队列管理器并监听所谓的发起队列(initiation queue)。队列管理器被配置为在这个队列上放置一条消息,以响应在 WebSphere MQ File Transfer Edition 代理的命令队列(command queue)中到达的一条消息。收到触发消息时,触发监控器解析将执行的命令以及任何参数的消息。这些内容被组装到一个正在运行的命令行中,然后控制在其发起队列上恢复监控的触发监控器的返回结果。

Perl 代码是必要的,因为触发监控器假设将要被启动的程序已被编写来将进行触发,并将整个 TMC2 触发消息作为一个参数传递。当然,fteStartAgent 程序是为人类、而不是触发监控器编写的,没有一个 TMC2 结构看起来怎样的概念。因此,如果直接由触发监控器启动,它将不能运行。一个小型 Perl 程序充当 fteStartAgent 和触发监控器之间的一个 “垫片”,使用 TMC2 参数并将其转换为 fteStartAgent 本机参数值。Perl 因其字符串处理功能而被选中,且它被安装在所有版本的 Windows 服务器上,且随时可用。它也可以被编译为一个本机 Windows 可执行文件,这种文件无需安装 Perl 即可运行。

最后的步骤是配置队列管理器以进行触发。这包括定义一个进程对象和在代理命令队列上启用触发。进程定义包括将用于启动代理的完全限定命令,以及将被启动的代理的名称。

当我们在这个项目上安装第一个代理触发监控器后,我们发现了其他一些好处。单个触发监控器能够处理同一个 Windows 服务器上的任意数量的代理。使用触发监控器还意味着 WebSphere MQ 管理员可以从队列管理器启动或停止远程客户端代理,而无需登录运行代理的 Windows 服务器。我的客户遇到的一个问题是 WebSphere MQ 管理员不能直接访问 Windows 服务器,在这种情况下,远程启动和停止代理的能力尤其有用。

下面的步骤将帮助您设置您自己的 Windows 客户端触发监控器,以启动 WebSphere MQ File Transfer Edition 代理。这些说明假定 WebSphere MQ File Transfer Edition 客户端代理已安装到 C:\IBM\WMQFTE 且配置目录是 C:\IBM\WMQFTE\config。我发现,如果使用没有嵌入空格的短路径名,那么在 Windows 上管理 WebSphere MQ File Transfer Edition 将容易得多。运行触发监控器的帐户的路径中应该有 C:\IBM\WMQFTE\bin 和 Perl 运行时。在开始安装本文介绍的工具之前,最好配置 WebSphere MQ File Transfer Edition 代理并确保其正确运行。

设置并测试一个 Windows 客户端触发监控器

  1. 安装 fteTriggerAgent.pl

    下载 fteTriggerAgent.pl 文件(参见 参考资料 或下面的清单 3)并将其放置到 WebSphere MQ File Transfer Edition 代理将运行的服务器上的 C:\IBM\WMQFTE\bin 目录中。

    这个脚本假定 WebSphere MQ File Transfer Edition 安装在 C:\IBM\WMQFTE\ 中,并试图将日志文件写入 C:\IBM\WMQFTE\Logs。如果上述路径不存在,脚本将尝试创建它。如果您希望来自 fteTriggerAgent.pl 脚本的日志文件被写入到一个不同的目录,则需要更改位于脚本顶部附近的 $LogPath 变量。

    这个脚本文件应该与 C:\IBM\WMQFTE\bin 中的其余文件拥有相同的所有权和权限。要运行此脚本,.pl 扩展名应该与 Perl 可执行文件关联。(如果您想将这个脚本编译为一个可执行文件,请查看来自 Active State 的 Perl Pro Studio。)

  2. 安装 WebSphere MQ 客户端

    如果您还没有在将运行 WebSphere MQ File Transfer Edition 客户端的服务器上安装 WebSphere MQ 客户端软件,请获取该软件的最新版。目前,最新版为 SupportPac MQC7,位于 SupportPacs 主页上(参见 参考资料)。作为管理员登录,并按照 WebSphere MQ Quick Beginnings for Windows 文档中的说明安装软件。

    检查 Recommended Fixes for WebSphere MQ 页面检查是否存在比您刚才安装的版本更新的补丁包。如果是,下载它并根据该补丁包提供的说明安装。

  3. 安装 SupportPac MA7K

    从 SupportPacs 主页下载 SupportPac MA7K。

    将该文件解压缩到将运行 WebSphere MQ File Transfer Edition 代理的 Windows 服务器上。最佳位置是与 IBM 父目录下的 WMQFTE 目录相邻。例如,如果 WebSphere MQ File Transfer Edition 安装到 C:\IBM\WMQFTE 目录,则将 MA7K 安装到 C:\IBM\MA7K。这并非是强制要求,但这使管理员可以更轻松地在相邻目录中定位这些相关产品。

    触发监控器使用一个设置程序来安装,该设置程序从一个 .ini 文件读取配置。编辑 setup.ini 文件,以提供服务帐户、连接和发起队列的细节信息。清单 1 展示了一个样例。

    清单 1
    Global:
    ShortTmr=60
    ShortRty=10
    LongTmr=1200
    LongRty=999999999
    EventLevel=2
    WaitInterval=60000
    
    ** Note that the file is a kdb format and the extension is not specified
    KeyRepository=C:\IBM\WMQFTE\config\<coordination qmgr>\ssl\key
    
    ** Specify the mqfte service userid (in the form: domain\user)
    ** The setup program will prompt for a password.
    ** If the user is invalid, you will receive a "Error number 1057" at setup time
    ** Ensure the user has rights to logon as a service
    
    ServiceUserid=.\mqfte
    
    MQSeriesDLL=mqic32.dll
    
    ** For each thread to run, there is a "thread" stanza, maximum 16 stanzas
    
    Thread:
    TriggerQueueName=SYSTEM.FTE.INITQ.<hostname>
    TriggerQueueMgrName=<agent QMgr>
    CONNAME=<hostname(port)>
    CHANNEL=FTE.SSL.SVRCONN
    SSLCIPH=TRIPLE_DES_SHA_US

    继续操作之前,我们先来看看 setup.ini 文件中的几个项目:

    • 表单 domain\user 中的服务用户 ID。在 setup.ini 文件中,值 “.\mqfte” 表示使用在本地主机上定义的 mqfte 帐户。如果您使用的是一个不同的服务帐户,一定要更新 setup.ini 文件中的这一行。
    • 在这个演示中,服务被设置为使用 SSL 通道。无论如何,请您一定要为代理和代理触发监控器使用 SSL。但是,对于初始测试,可以在必要时通过禁用 SSL 来消除来自这个等式的任何 SSL 错误。稍后一定要记得重新启用 SSL。
    • 由于触发监控器是一个 C 程序而代理是 Java 程序,因此它们使用不同的密匙存储类型。可以将代理的 Java 密匙存储转换为一个 kdb 密匙存储并使用相同的证书,也可以为触发监控器使用一个不同的证书。在本例中,密匙存储是一个名为 key.kdb 的标准 kdb 文件,一个存储文件命名为 key.sth
    • 我选择对每个主机而不是每个代理使用一个发起队列。这允许一个指定主机上的触发监控器服务该主机上的多个 WebSphere MQ File Transfer Edition 代理。
  4. 安装触发监控器服务。

    从一个命令提示,导航到 MA7K 的安装目录和 setup.ini 文件驻留的目录。通过运行 setup 命令并指定 setup.ini 文件来安装此服务:

    setup -f setup.ini

    在提示时输入 mqfte 服务帐户的密码。(注意,“mafte” 是一个虚构的服务帐户,这个名称没有任何特别意义。可以使用任意服务帐户,只要该帐户没有管理员权限。记住,触发监控器将执行从发起队列传递给它的任何命令,因此有必要限制能够将消息放置到发起队列的人员并以非管理员用户身份运行触发监控器。)

    安装过程运行之后,验证此服务出现在 Services applet 中(见图 1)。此服务将以 Automatic Start 模式安装,这意味着 Windows 不尝试启动此服务,因此尽管服务在服务面板中可见,但其状态栏将为空。

    将在启动时尝试启动服务,这是理想的行为。但是,设置程序本身并
    图 1. 安装触发监控器服务
    图 1. 安装触发监控器服务
    图 1. 安装触发监控器服务

    启动服务并等待它初始化。与服务启动和运行相关的任何错误将导致出现一个服务已经失败的对话框。大多数情况下可能会导致这些事件在设置过程中被捕获。另一方面,此服务可能拥有连接到队列管理器或访问其发起队列的相关问题。这些运行时错误将在 Windows 事件日志中报告。如果在启动服务 applet 时报告任何错误,请查看 Windows 事件日志。在这个阶段,您将看到一些表明通道或发起队列不存在的错误。这在预料之中,这些对象将在下一小节中创建。现在,只需停止服务。

    一旦确认服务正确启动,就需要修改服务定义,以便服务自动重新启动。设置重新启动的第一、第二和后续动作,并将间隔时间设置为 10 分钟,如图 2、3 和 4 所示。

    图 2. 常规服务启动
    图 2. 常规服务启动
    图 2. 常规服务启动
    图 3. Logon 选项卡
    图 3. Logon 选项卡
    图 3. Logon 选项卡
    图 4. Recovery 选项卡
    图 4. Recovery 选项卡
    图 4. Recovery 选项卡
  5. 配置队列管理器

    单个触发监控器能够服务同一主机上的任意数量代理,只要它们使用相同的 Agent QMgr。在队列管理器上,这转化为要求其上驻留一个代理的每个主机有一个启动队列。出于这个原因,我选择一个发起队列命名惯例 SYSTEM.FTE.INITQ.<hostname>,其中 hostname 为大写且小于或等于 22 个字符。您可以对发起队列使用任意队列名称,但出于安全考虑,我强烈反对使用 SYSTEM.DEFAULT.INITIATION.QUEUE。

    有一点值得一提,选择队列名称 SYSTEM.FTE.INITQ.* 可能会与产品未来使用的队列名称冲突。一般原则是,选择对象名称 SYSTEM.* 意味着您必须在更新到新产品版本时随时监控冲突。如果您想完全避免冲突问题,请为您的发起队列选择一个不以 SYSTEM 开始的名称。确保更新触发监控器的 setup.ini 文件和代理的命令队列中的队列名称引用。

    由于每个代理都有一个进程对象,我选择的命名惯例将代理名称嵌入到进程名称中。进程定义被命名为 SYSTEM.FTE.<agent name>,其中代理名称匹配 SYSTEM.FTE.COMMAND.<agent name> 队列中使用的代理名称大小写和拼写。上述关于选择包含 SYSTEM.* 的名称的建议在这里也适用。这也许不是一个大问题,但如果您不放心,请选择一个不同的名称。

    在队列管理器上启动触发机制需要定义两个对象并改变现有的命令队列,如清单 2 所示。

    清单 2
    DEFINE QLOCAL(SYSTEM.FTE.INITQ.<hostname>) +
           DESCR('FTE initiation queue for <hostname>') +
           REPLACE
    
    DEFINE PROCESS(SYSTEM.FTE.<agent name>) +
           APPLTYPE(WINDOWSNT) +
           APPLICID('C:\IBM\WMQFTE\bin\fteTriggerAgent.pl -v') +
           ENVRDATA('C:\IBM\WMQFTE\bin\fteStartAgent.cmd') +
           USERDATA('<agent name>') +
           DESCR('Trigger process for <agent name>') +
           REPLACE
    
    ALTER  QLOCAL(SYSTEM.FTE.COMMAND.<agent name>) +
           INITQ(SYSTEM.FTE.INITQ.<hostname>) +
           PRO(SYSTEM.FTE.<agent name>) +
           TRIGGER +
           TRIGTYPE(FIRST)

    记住,触发器只在以下时刻触发:

    • 代理命令队列上没有打开的输入句柄;
    • 且发起队列上有一个打开的输入句柄;
    • 且代理命令队列中的队列深度从 0 变为 1。

    最后一条比较麻烦。当您启动触发监控器时,如果代理命令队列中有一些消息,则触发器将不会触发。或者,确切说来,触发器在 TRIGINT 毫秒数流逝之前不会触发,TRIGINT 的默认值为 999,999,999,即 278 小时。要确保您的代理及时触发,要么在启动触发监控器之前清除代理命令队列,要么将 TRIGINT 设置为一个更实用的间隔。

  6. 测试驱动您的新触发监控器服务

    MA7K 触发监控器服务 WebSphere MQ File Transfer Edition 代理非常具有弹性。fteStopAgent 命令还有效,但触发监控器将立即自动重新启动代理,原因是 WebSphere MQ File Transfer Edition 代理在它关闭时在其命令队列中放入一个占位符消息。当代理释放器命令队列上的输入句柄时,则队列管理器检测到命令队列有一个大于 0 的深度,从而发出一个新的触发器事件,然后代理重新启动。尝试多次发送 fteStopAgent 以感觉它是如何工作的。如果您想在代理关闭时捕获它,您必须动作迅速,因为代理监控器几乎是立即重新启动它。如果有疑问,请检查代理日志文件,您将看到它正在停止并重新启动。

    如果您想有意识地停止 WebSphere MQ File Transfer Edition 代理,需要首先在代理的命令队列上禁用触发机制,然后运行 fteStopAgent 命令。要远程重新启动代理,只需在代理的命令队列上重新启用触发机制。您可以等待,让它在一条命令到达时启动;也可以向它发送任何命令,比如 ftePingAgent 或者甚至 fteStopAgent(该命令将一个 Stop 命令放置到代理的命令队列上,从而导致触发器触发),触发监控器将启动代理。

结束语

MA7K Windows 触发监控器服务是在 Windows 服务器启动时自动启动 WebSphere MQ File Transfer Edition 客户端代理的一个好方法。除了启动代理外,触发监控器将在代理关闭或从命令队列断开连接时重新启动代理。如果触发监控器设置为自动重新启动,则整个配置将变得非常有弹性。此服务还有一个额外的好处,触发 WebSphere MQ File Transfer Edition 代理允许 WebSphere MQ 管理员通过在代理的命令队列上启用和禁用代理来启动和停止它。

清单 3. fteTriggerAgent.pl
#----------------------------------------------------------------------#
# fteTriggerAgent.pl
#
# Windows program to start a triggered FTE agent
#
# Script parses a WMQ TMC2 tigger message and starts the named FTE agent
# specified in the process definition.  Uses unpack so that the MQSeries
# Perl module is not required.
#
# The command constructed from the process definition is as follows:
# Drive:\path to\fteStartAgent AgentName
#
# The $EnvData contains the fully-qualified executable name which must
# end with fteStartAgent.cmd
#
# The $UserData field must contain the agent name.
#
#
#----------------------------------------------------------------------#
# History
# 20100125 T.Rob - New script
#
#
#----------------------------------------------------------------------#
use strict;
use Fcntl qw(:flock);
use File::Path 'mkpath';
$| = 1; # Turn on Autoflush
($0) = (split(m|[/\\]|, $0))[-1]; # Normalize $0

my $Testing = 1; # Set to 0 to disable dump of trigger messages
my $LogFile;

# Set up the log file name
{
     my ($Min, $Hr, $MDay, $Mon, $Year) = (localtime(time))[1..5];
     $Year = $Year %100; $Mon++;
     my $TimeStamp = sprintf("%02d%02d%02d",$Year,$Mon,$MDay);
     my $LogPath = 'C:\\IBM\\WMQFTE\\Logs';
     my @Created = mkpath( $LogPath );
     $LogFile = "$LogPath\\$0.$TimeStamp.log";
}

# Trigger Message    struct tagMQTMC2 {
 my ($StrucId,      #   MQCHAR4    StrucId;      /* Structure identifier    */
     $Version,      #   MQCHAR4    Version;      /* Structure version number*/
     $QName,        #   MQCHAR48   QName;        /* Name of triggered queue */
     $ProcessName,  #   MQCHAR48   ProcessName;  /* Name of process object  */
     $TriggerData,  #   MQCHAR64   TriggerData;  /* Trigger data            */
     $ApplType,     #   MQCHAR4    ApplType;     /* Application type        */
     $ApplId,       #   MQCHAR256  ApplId;       /* Application identifier  */
     $EnvData,      #   MQCHAR128  EnvData;      /* Environment data        */
     $UserData,     #   MQCHAR128  UserData;     /* User data               */
     $QMgrName,     #   MQCHAR48   QMgrName;     /* Queue manager name      */
     );             # };

&Log("$0 Started", exists($ENV{'COMPUTERNAME'}) ? " on $ENV{'COMPUTERNAME'}" : '');
foreach (@ARGV) {
     if (/^TMC    2/) { # Ignore all parms but TMC2 itself
          ($StrucId, $Version, $QName, $ProcessName, $TriggerData, $ApplType, 
		$ApplId, $EnvData, $UserData, $QMgrName,)
               = unpack("a4 a4 a48 a48 a64 a4 a256 a128 a128 a48", $_);
          $QName    =~ s/\s+$//; # delete trailing spaces
          $ApplId   =~ s/\s+$//; # delete trailing spaces
          $UserData =~ s/\s+$//; # delete trailing spaces
          $EnvData  =~ s/\s+$//; # delete trailing spaces
          $QMgrName =~ s/\s+$//; # delete trailing spaces
      
          &DumpTriggerMsg if $Testing;
      
          # Verify the $EnvData parm
          my $Path;
          if ($EnvData =~ /^(.*)\\bin\\fteStartAgent.cmd$/ && -e $EnvData) {
               $Path = $1;
          } else {
               &Log("$0: Bad command in process ENVRDATA: '$EnvData'");
          }
      
          my @Exec = ($EnvData, $UserData);
          &Log(join(" ", @Exec), "\n");
          system(@Exec);
          last;
     } else {
          if (/^-v/i) {
               $Testing = 1;
               &Log("Verbose mode enabled on command line.");
          } else {
               &Log("Parm not a TMC2 message = '$_'");
          }
     }
}
&Log("$0 Ended.\n\n");
exit 1; # Non-zero exit for trigger monitor

sub DumpTriggerMsg {
     &Log("\$0          = '$0");
     &Log("StrucId      = '$StrucId'");
     &Log("Version      = '$Version'");
     &Log("QName        = '$QName'");
     &Log("ProcessName  = '$ProcessName'");
     &Log("TriggerData  = '$TriggerData'");
     &Log("ApplType     = '$ApplType'");
     &Log("ApplId       = '$ApplId'");
     &Log("EnvData      = '$EnvData'");
     &Log("UserData     = '$UserData'");
     &Log("QMgrName     = '$QMgrName'\n");
}

sub Log {
     my ($Sec, $Min, $Hr, $MDay, $Mon, $Year) = (localtime(time))[0..5];
     my $TimeStamp = sprintf("%04d%02d%02d-%02d:%02d:%02d: ",($Year+1900),
	++$Mon,$MDay,$Hr,$Min,$Sec);

     open LOG, ">>$LogFile";
     # Wait up to 5 seconds for an exclusive lock on log file, else skip logging
     my $Sleep = 5;
     while ($Sleep) {
          # Non-Blocking check for an exclusive lock
          if (flock(LOG, LOCK_EX|LOCK_NB)) {
               print LOG $TimeStamp, @_, "\n";
               $Sleep = 0;
          } else {
               --$Sleep;
          }
     }
     close LOG;
}

下载资源


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=547949
ArticleTitle=任务:消息: 使用一个 Windows 服务来启动 WebSphere MQ File Transfer Edition 客户端代理
publish-date=09252010