Showing posts with label MQ. Show all posts
Showing posts with label MQ. Show all posts

Monday, August 31, 2009

WAS 6与Websphere MQ6的整合

更多精彩请到 http://www.139ya.com

WAS 6与Websphere MQ6的整合

2007-08-29 19:59作者:出处:天极Yesky软件频道责任编辑:半仙

  整合的目的是让MQ作为websphere application server的JMS provider,所有的JMS消息会被was转发到MQ队列中,由于MQ提供了多种平台的API,因此消息的传递可以不再通过转发的方式。任何平台都可 以直接连接到MQ获取/发送消息,减少了跨平台消息传递中的不确定因素。

  首先在MQ中建立队列管理器、消息队列、服务器连接通道(SYSTEM.DEF.SVRCONN类型的通道)。由于was将通过CLIENT模式与MQ通信,所以有身份认证的问题,需要在通道上配置MAC用户标识,它的值应该是运行MQ的操作系统的 一个用户名称,该用户应该具有管理员权限,推荐使用启动MQ的用户名称。如果不配置该MAC用户标识,那么运行was的机器必须建立一个与运行MQ完全相 同的用户/密码来启动was,才能提供正确的身份信息。如果采用BINDINF的传输模式,就不需要任何的认证信息,但是该模式只能适用于was和MQ在 同一台机器上的情况。而且一个was环境中,只能允许一个连接工厂使用BINDING模式连接,所以不推荐该模式。

  假设建立起来的队列管理器为sobeymam,ccsid为1208(Unicode)或者1381(GB2312),通信端口为1414;建立的消息队列为test,ccsid为1208(Unicode);服务器连接通道为s1,MAC用户标识为bromon。

  下一步是在WAS中建立队列连接工厂,“资源”----“JMS提供者”----“Websphere MQ”----“Websphere MQ队列连接工厂”----“新建”。填写以下内容:

  名称: cf

  JNDI名称: jms/cf

  队列管理器:  mam

  主机: 运行MQ的机器名或IP

  端口: 1414(与队列管理器的侦听端口对应)

  通道: s1(与队列管理器的服务器连接通道对应)

  传输类型: CLIENT

  取消“已起用XA”选项

  连接工厂的配置完成。

  下一步是建立队列目标。“资源”----“JMS提供者”----“Websphere MQ”----“Websphere MQ队列目标”----“新建”。填写以下内容:

  名称: test

  JNDI名称: jms/test

  基本队列名:  test(对应MQ中的本地队列)

  CCSID: 1208

  目标客户机:   MQ

  队列目标的配置完成。需要说明的是,目标客户机的值决定了JMS的消息将以什么格式存放在MQ中。如果是”MQ”,那么消息将直接使用 MQSTR格式,纯文本的存放,任何平台都能够获取消息直接显示。如果是”JMS”,那么消息将使用MSHRF2格式存放,java平台能够不加处理的正 常识别,其他平台必须要做转换才能正确获得消息文本。推荐使用”MQ”。

  如果需要部署消息BEAN获取MQ中的消息,那么还需要配置侦听器端口,“服务器”----“应用服务器”----“server1”----“通信”----“消息传递”----“消息侦听器服务”----“侦听器端口”----“新建”。填写一下内容:

  名称: test

  连接工厂JNDI名: jms/cf(对应websphere队列连接工厂的JNDI)

  目标JNDI名: jms/test(对应websphere队列目标的JNDI)

  侦听器端口配置完成,部署消息bean时需提供该侦听器名称。

  重启websphere使配置生效。

  编写一个jsp尝试发送消息:








<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*"%>
<%@ page import="javax.jms.*"%>
<%@ page import="javax.naming.*" %>
<%
try
{
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.websphere.naming.WsnInitialContextFactory");
p.put(Context.URL_PKG_PREFIXES, "com.ibm.ws.naming");
p.put(Context.PROVIDER_URL, "iiop://localhost:2809/");
InitialContext ctx = new InitialContext(p);
QueueConnectionFactory qcf =
(QueueConnectionFactory) ctx.lookup("jms/cf");//连接工厂
QueueConnection conn = qcf.createQueueConnection();
Queue q = (Queue) ctx.lookup("jms/test");//队列
QueueSession s = conn.createQueueSession(
false,QueueSession.AUTO_ACKNOWLEDGE);
conn.start();
QueueSender sender = s.createSender(q);
TextMessage tm=s.createTextMessage(
"测试websphere mq有中文字符");//创建文本消息
sender.send(tm, DeliveryMode.PERSISTENT, 4, 0);
sender.close();
}catch(Exception e)
{
out.println(e);
}
%>

Wednesday, August 26, 2009

WebShpere MQ 配置与测试

更多精彩请到 http://www.139ya.com

From : http://blog.sina.com.cn/s/blog_49a75433010008d5.html

在“WebSphere
MQ程序设计初探”一文中,讨论了从MQ队列管理器的本地队列中放置和读出消息的程序,本文主要通过两台机器,搭建MQ消息传输的环境,并编写测试程序进行测试。
第一、准备工作
准备2台Win2000环境(XP也可),通过以太网连通。
机器A:代码为00000000,IP地址为:10.1.1.1
机器B:代码为88888888,IP地址为:10.1.1.2
安装MQ 5.3

第二、创建MQ对象
A机器上:
1、打开“WebSphere MQ资源管理器”,新建队列管理器,名称为QM_00000000,其余采用默认设置;
2、在QM_00000000队列管理器中创建本地队列,名称为LQ_00000000;
3、创建传输队列,名称为XQ_88888888(新建时选择“本地队列”,将“用法”设置为“传输”);
4、创建远程队列定义,名称为RQ_88888888,指定远程队列名称为LQ_88888888,远程队列管理器名称为QM_88888888,传输队列名称为XQ_88888888;
5、创建发送方通道,名称为00000000.88888888,传输协议为TCP/IP,连接名称为10.1.1.2(1414),传输队列为XQ_88888888;
6、创建接受方通道,名称为88888888.00000000,采用默认设置;
7、创建服务器连接通道,名称为DC.SVRCONN,采用默认设置(该通道主要给后面的测试程序使用)。
B机器和A机器上的操作一样,只是命名不同,如下:
1、打开“WebSphere MQ资源管理器”,新建队列管理器,名称为QM_88888888,其余采用默认设置;
2、在QM_88888888队列管理器中创建本地队列,名称为LQ_88888888;
3、创建传输队列,名称为XQ_00000000(新建时选择“本地队列”,将“用法”设置为“传输”);
4、创建远程队列定义,名称为RQ_00000000,指定远程队列名称为LQ_00000000,远程队列管理器名称为QM_00000000,传输队列名称为XQ_00000000;
5、创建发送方通道,名称为88888888.00000000,传输协议为TCP/IP,连接名称为10.1.1.1(1414),传输队列为XQ_00000000;
6、创建接受方通道,名称为00000000.88888888,采用默认设置;
7、创建服务器连接通道,名称为DC.SVRCONN,采用默认设置。

第三、消息测试
在A、B机器上分别启动其发送方通道,正常情况通道状态应为“正在运行”。
通过如下测试程序进行测试,文件名为:MQTest.java,在机器A上进行运行(如在B上运行请读者自行适当修改)。
-------------------------------------------------------------------------------------------
import java.io.IOException;
import java.util.Hashtable;

import com.ibm.mq.MQException;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;

public class MQSample{
//定义队列管理器和队列的名称
private static String qmName = "QM_00000000";
private static String qName = "RQ_88888888";

private static MQQueueManager qMgr;
private static Hashtable properties = new Hashtable();

public static void main(String args[]) {
try {
properties.put("hostname", "10.1.1.1");
properties.put("port", new Integer(1414));
properties.put("channel", "DC.SVRCONN");
properties.put("CCSID", new Integer(1381));
properties.put("transport","MQSeries");

// Create a connection to the queue manager
qMgr = new MQQueueManager(qmName,properties);
// Set up the options on the queue we wish to open...
int openOptions = 16;
// Now specify the queue that we wish to open,
// and the open options...
MQQueue remoteQ = qMgr.accessQueue(qName, openOptions);

// Define a simple WebSphere MQ message, and write some text in UTF format..
MQMessage putMessage = new MQMessage();
putMessage.writeUTF("Test");
// specify the message options...
MQPutMessageOptions pmo = new MQPutMessageOptions();
// accept the defaults, same as MQPMO_DEFAULT
// put the message on the queue
remoteQ.put(putMessage,pmo);
System.out.println("Message has been input into the Remote Queue");

// Close the queue...
remoteQ.close();
// Disconnect from the queue manager
qMgr.disconnect();
}catch (MQException ex) {
// If an error has occurred in the above, try to identify what went wrong
// Was it a WebSphere MQ error?
System.out.println("A WebSphere MQ error occurred : Completion code " + ex.completionCode +
" Reason code " + ex.reasonCode);
}catch (IOException ex) {
// Was it a Java buffer space error?
System.out.println("An error occurred whilst writing to the message buffer: " + ex);
}catch(Exception ex){
ex.printStackTrace();
}
}
}

Wednesday, July 29, 2009

mq经验总结

更多精彩请到 http://www.139ya.com

转自 : http://www.betalog.cn/post/247.html

mq经验总结
首先了解什么是mq?mq的作用是什么?
mq是通讯中间件。他的作用是省去开发人员开发通讯工具的时间,节省开发成本,提高开发效率。
mq的使用,如何安装mq?
根据以往的经验,win版的mq比较容易安装,傻瓜式,一路next就可以。
aix版本的用smitty安装。
linux版本用rpm -ivh 安装
mq中一些名称的概念:
队列管理器:简单的说就是一个大容器的管理员,这个大容器里放了很多东西。
队列:大容器里的东西,存放消息的盒子。
通道:大容器和大容器之间,程序和容器之间进行通讯的途径。
mq是如何实现通讯的?
mq的通讯方式有两种,通俗的说就是mq之间进行通讯,开发的程序和mq之间的通讯。
mq之间进行通讯:通过发送接收通道建立tcp连接进行消息传输,称为server对server
开发的程序和mq之间的通讯:通过服务器连接通道进行传输,client对server
如何配置两台mq使之相互进行通讯?
首先要规划好两个队列管理器之间使用的ip和端口,假设我们使用
ip 端口
192.168.0.1 1414
192.168.0.2 1415
第一步 建立队列管理器
crtmqm -lc -lf 100 -lp 3 -ls 3 QM1
解释下:
-lc 是采用循环日志
-lf 是每块日志的大小,4k为单位的,100就是100*4k
-lp 是主逻辑日志的数量
-ls 是辅逻辑日志的数量
QM1 是队列管理器名称
第二步 启动队列管理器
strmqm QM1
第三步 定义队列管理器中的队列和通道等
先运行runmqsc QM1首先要保证运行该命令的用户属于mqm组
运行完后进入mq命令窗口
定义本地队列 def ql(QL1)
先解释什么是本地队列,然后解释命令的含义(以下同)
本地队列是存储信息的盒子,用户可以从本地队列里取消息,对方发送消息的目的地也是本地

队列。
def是 define的缩写,mq支持一些命令的缩写。
ql是queue local的缩写,表示本地队列,括号内是本地队列名
定义远程队列 def qr(QR1) rname(QL2) rqname(QM2) xmitq(QT1)
远程队列是相对于本地队列的,当用户希望往另一个队列管理器发消息的时候,配置好远程队

列,用户直接放消息到该队列就可以,mq会传输到另一方的本地队列中。
以上面的例子说明,当我们把消息放入该远程队列后,消息会传输到QM2队列管理器中的QL2队

列中。
qr queue remote的缩写
rname 指定的远程队列管理器上的队列名
rqname 远程队列管理器
xmitq 所要用的传输队列

定义传输队列 def ql(QT1) usage(xmitq) trigger trigtype(first) initq

(system.channel.initq) trigdata(QM1.QM2)
传输队列是传输的介质,消息是通过传输队列进行传输的。
usage 用途xmitq是传输队列
trigger 消息触发开关
trigtype 触发类型第一条消息触发
initq 初始队列
trigdata 触发数据

定义发送通道 def chl(QM1.QM2) chltype(sdr) conname('192.168.0.2(1415)') trptype

(tcp) xmitq(QT1)
发送通道就相当于建立一个tcp的连接
chl channel的缩写
chltype 通道类型sdr是发送通道
conname 连接名包括对方的ip和端口
trptype 通讯类型tcp通讯
xmitq 使用的传输队列

定义接受通道 def chl(QM2.QM1)
接收通道是被动的,只定义名字就可以。大家注意,接收通道的名字一定要和发送通道名一致

,他们是靠名字来匹配。

第四步 配置监听器
是对方mq管理器来探测,本地要给对方一个回应,监听器就是起这个作用的。
如果是5.3版本 只能在命令行里运行 runmqlsr -m QM1 -t tcp -p 1414
如果是6.0版本 可以runmqsc QM1里运行 def listener(LSR.QM1) trptype(tcp) port(1414)

control(qmgr)
解释下 trptype 监听类型
port 监听端口
control 监听控制,如果是qmgr则在队列管理器启动的时候监听也自动启动。

第五步 配置另外一个队列管理器
简单的说一下,和上面的差不多,只不过名字不一样。大家自己尝试下:)
写的手累了,下次补充!

继续
上面我们说完了如何建队列管理器,接下来我们说说建完以后如何测试两边是不是正常传输,
我们可以用命令行方式向远程队列中放入测试消息,以aix为例,
用/usr/mqm/samp/bin/amqsput命令就可以放入消息,格式为:
amqsput QR1 QM1
解释下:QR1是你要放入的队列名,QM1是你要放入的队列管理器名。
输入以上命令后就可以写入消息了,一下回车就是发送一条消息,两次回车就是退出。

用/usr/mqm/samp/bin/amqsget命令就可以取消息,格式为
amqsget QL2 QM2
解释下:QL1是你要取消息的队列名,QM2是你要取消息的队列管理器名。
输入完命令后就会显示所有消息。
不过要注意一点,命令行方式的输入和取消息有字节限制。

如果只是简单的浏览一下消息可以使用
/usr/mqm/samp/bin/amqsbcg命令
格式和取消息一样,但是该命令不会把消息取出来,运行完该命令后消息还是保存在队列中。

正常情况下消息的传输流程(只说正常的,排错一会再说)
QR1 -> QT1 -> QL2
消息被放入到远程队列中,远程队列通过传输队列传输,最后传输到QM2中的本地队列。

出现问题,我们怎么办?
1 不能放入消息。
一般这种情况应该大部分是远程队列中的传输队列那个参数配置的不正确。
还有可能是队列的允许放入这个参数设置成了禁止。基本上就这两种情况。

2 发送通道和接收通道的状态不是running
首先说明,如果长时间没有消息传输,通道的状态会变成不活动状态,这是正常现象。
如果你手动启动通道后,通道状态还不是running,那先查看错误日志(两边的队列管理器都要查看)
/var/mqm/qmgrs/QM1/errors中的错误日志,通常编号01的日志是最新日志。
常见情况是网络不通导致的通道不通!所以首先要保证网络是正常的,我们可以同过telnet对方的IP加监听端口的方法来查看是不是正常。
telnet 192.168.0.2 1415

再有的情况是两边的配置属性有问题,如两边发送和接收通道名不一致,发送通道的连接名配置错误,发送通道中的传输队列配置错误。
我们也可以执行mq中的一个命令来查看通道是不是正常
runmqsc QM1
ping chl(QM1.QM2)
ping操作来查看两边的通道是不是正常,如果正常会返回ping完成。

3 放入的消息没有到QM2的队列中
注意:消息一定要放入远程队列中,如果放入传输队列中消息会被放入死信队列中。(上面忘记定义死信队列了,晕)
再有看看远程队列中的属性是不是配置错误,如rname,rqname,xmitq等属性。
也有可能是发送接收队列的消息序列号不一致。如果不一致做一下reset操作。
还有可能是上一批消息没有提交。可以做一个resolve操作。
也是要先看错误日志

4 消息到达QM2队列QL2中,但是取不出来
QL2的允许取出属性是不是被禁止了。

这样再查看以下QM2到QM1的传输是不是正常。都正常就OK了。

消息中间件及WebSphere MQ入门

更多精彩请到 http://www.139ya.com

转自 : http://download.zdnet.com.cn/software_zone/2007/1005/536602.shtml

消息队列技术是分布式应用间交换信息的一种技术。消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走。通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。

  消息中间件概述

  消息队列技术是分布式应用间交换信息的一种技术。消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走。通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。

  在分布式计算环境中,为了集成分布式应用,开发者需要对异构网络环境下的分布式应用提供有效的通信手段。为了管理需要共享的信息,对应用提供公共的信息交换机制是重要的。

  设计分布式应用的方法主要有:远程过程调用(PRC)--分布式计算环境(DCE)的基础标准成分之一;对象事务监控(OTM)--基于CORBA的面向对象工业标准与事务处理(TP)监控技术的组合;消息队列(MessageQueue)--构造分布式应用的松耦合方法。

  (a) 分布计算环境/远程过程调用 (DCE/RPC)

  RPC是DCE的成分,是一个由开放软件基金会(OSF)发布的应用集成的软件标准。RPC模仿一个程序用函数引用来引用另一程序的传统程序设计方法,此引用是过程调用的形式,一旦被调用,程序的控制则转向被调用程序。

  在RPC实现时,被调用过程可在本地或远地的另一系统中驻留并在执行。当被调用程序完成处理输入数据,结果放在过程调用的返回变量中返回到调用程序。RPC完成后程序控制则立即返回到调用程序。因此RPC模仿子程序的调用/返回结构,它仅提供了Client(调用程序)和Server(被调用过程)间的同步数据交换。

  (b) 对象事务监控 (OTM)

  基于CORBA的面向对象工业标准与事务处理(TP)监控技术的组合,在CORBA规范中定义了:使用面向对象技术和方法的体系结构;公共的Client/Server程序设计接口;多平台间传输和翻译数据的指导方针;开发分布式应用接口的语言(IDL)等,并为构造分布的Client/Server应用提供了广泛及一致的模式。

  (c) 消息队列 (Message Queue)

  消息队列为构造以同步或异步方式实现的分布式应用提供了松耦合方法。消息队列的API调用被嵌入到新的或现存的应用中,通过消息发送到内存或基于磁盘的队列或从它读出而提供信息交换。消息队列可用在应用中以执行多种功能,比如要求服务、交换信息或异步处理等。

  中间件是一种独立的系统软件或服务程序,分布式应用系统借助这种软件在不同的技术之间共享资源,管理计算资源和网络通讯。它在计算机系统中是一个关键软件,它能实现应用的互连和互操作性,能保证系统的安全、可靠、高效的运行。中间件位于用户应用和操作系统及网络软件之间,它为应用提供了公用的通信手段,并且独立于网络和操作系统。中间件为开发者提供了公用于所有环境的应用程序接口,当应用程序中嵌入其函数调用,它便可利用其运行的特定操作系统和网络环境的功能,为应用执行通信功能。

  如果没有消息中间件完成信息交换,应用开发者为了传输数据,必须要学会如何用网络和操作系统软件的功能,编写相应的应用程序来发送和接收信息,且交换信息没有标准方法,每个应用必须进行特定的编程从而和多平台、不同环境下的一个或多个应用通信。例如,为了实现网络上不同主机系统间的通信,将要求具备在网络上如何交换信息的知识(比如用TCP/IP的socket程序设计);为了实现同一主机内不同进程之间的通讯,将要求具备操作系统的消息队列或命名管道(Pipes)等知识。

  目前中间件的种类很多,如交易管理中间件(如IBM的CICS)、面向Java应用的Web应用服务器中间件(如IBM的Websphere Application Server)等,而消息传输中间件(MOM) 是其中的一种。它简化了应用之间数据的传输,屏蔽底层异构操作系统和网络平台,提供一致的通讯标准和应用开发,确保分布式计算网络环境下可靠的、跨平台的信息传输和数据交换。它基于消息队列的存储-转发机制,并提供特有的异步传输机制,能够基于消息传输和异步事务处理实现应用整合与数据交换。

  IBM 消息中间件MQ以其独特的安全机制、简便快速的编程风格、卓越不凡的稳定性、可扩展性和跨平台性,以及强大的事务处理能力和消息通讯能力,成为业界市场占有率最高的消息中间件产品。

  MQ具有强大的跨平台性,它支持的平台数多达35种。它支持各种主流Unix操作系统平台,如:HP-UX、AIX、SUN Solaris、Digital UNIX、Open VMX、SUNOS、NCR UNIX;支持各种主机平台,如:OS/390、MVS/ESA、VSE/ESA;同样支持Windows NT服务器。在PC平台上支持Windows9X/Windows NT/Windows 2000和UNIX (UnixWare、Solaris)以及主要的Linux版本(Redhat、TurboLinux等)。此外,MQ还支持其他各种操作系统平台,如:OS/2、AS/400、Sequent DYNIX、SCO OpenServer、SCO UnixWare、Tandem等。

  MQ的基本概念:

  1) 队列管理器

  队列管理器是MQ系统中最上层的一个概念,由它为我们提供基于队列的消息服务。

  2) 消息

  在MQ中,我们把应用程序交由MQ传输的数据定义为消息,我们可以定义消息的内容并对消息进行广义的理解,比如:用户的各种类型的数据文件,某个应用向其它应用发出的处理请求等都可以作为消息。消息有两部分组成:

  消息描述符(Message Discription或Message Header),描述消息的特征,如:消息的优先级、生命周期、消息Id等;

  消息体(Message Body),即用户数据部分。在MQ中,消息分为两种类型,非永久性(non-persistent)消息和永久性(persistent)消息,非永久性消息是存储在内存中的,它是为了提高性能而设计的,当系统掉电或MQ队列管理器重新启动时,将不可恢复。当用户对消息的可靠性要求不高,而侧重系统的性能表现时,可以采用该种类型的消息,如:当发布股票信息时,由于股票信息是不断更新的,我们可能每若干秒就会发布一次,新的消息会不断覆盖旧的消息。永久性消息是存储在硬盘上,并且纪录数据日志的,它具有高可靠性,在网络和系统发生故障等情况下都能确保消息不丢、不重。

  此外,在MQ中,还有逻辑消息和物理消息的概念。利用逻辑消息和物理消息,我们可以将大消息进行分段处理,也可以将若干个本身完整的消息在应用逻辑上归为一组进行处理。

  3) 队列

  队列是消息的安全存放地,队列存储消息直到它被应用程序处理。

  消息队列以下述方式工作:

  a) 程序A形成对消息队列系统的调用,此调用告知消息队列系统,消息准备好了投向程序B;

  b) 消息队列系统发送此消息到程序B驻留处的系统,并将它放到程序B的队列中;

  c) 适当时间后,程序B从它的队列中读此消息,并处理此信息。

  由于采用了先进的程序设计思想以及内部工作机制,MQ能够在各种网络条件下保证消息的可靠传递,可以克服网络线路质量差或不稳定的现状,在传输过程中,如果通信线路出现故障或远端的主机发生故障,本地的应用程序都不会受到影响,可以继续发送数据,而无需等待网络故障恢复或远端主机正常后再重新运行。

  在MQ中,队列分为很多种类型,其中包括:本地队列、远程队列、模板队列、动态队列、别名队列等。

  本地队列又分为普通本地队列和传输队列,普通本地队列是应用程序通过API对其进行读写操作的队列;传输队列可以理解为存储-转发队列,比如:我们将某个消息交给MQ系统发送到远程主机,而此时网络发生故障,MQ将把消息放在传输队列中暂存,当网络恢复时,再发往远端目的地。

  远程队列是目的队列在本地的定义,它类似一个地址指针,指向远程主机上的某个目的队列,它仅仅是个定义,不真正占用磁盘存储空间。

  模板队列和动态队列是MQ的一个特色,它的一个典型用途是用作系统的可扩展性考虑。我们可以创建一个模板队列,当今后需要新增队列时,每打开一个模板队列,MQ便会自动生成一个动态队列,我们还可以指定该动态队列为临时队列或者是永久队列,若为临时队列我们可以在关闭它的同时将它删除,相反,若为永久队列,我们可以将它永久保留,为我所用。

  4) 通道

  通道是MQ系统中队列管理器之间传递消息的管道,它是建立在物理的网络连接之上的一个逻辑概念,也是MQ产品的精华。

  在MQ中,主要有三大类通道类型,即消息通道,MQI通道和Cluster通道。消息通道是用于在MQ的服务器和服务器之间传输消息的,需要强调指出的是,该通道是单向的,它又有发送(sender), 接收(receive), 请求者(requestor), 服务者(server)等不同类型,供用户在不同情况下使用。MQI通道是MQ Client和MQ Server之间通讯和传输消息用的,与消息通道不同,它的传输是双向的。群集(Cluster)通道是位于同一个MQ 群集内部的队列管理器之间通讯使用的。

  MQ的工作原理

  

  

  如图所示:

  首先来看本地通讯的情况,应用程序A和应用程序B运行于同一系统A,它们之间可以借助消息队列技术进行彼此的通讯:应用程序A向队列1发送一条信息,而当应用程序B需要时就可以得到该信息。

  其次是远程通讯的情况,如果信息传输的目标改为在系统B上的应用程序C,这种变化不会对应用程序A产生影响,应用程序A向队列2发送一条信息,系统A的MQ发现Q2所指向的目的队列实际上位于系统B,它将信息放到本地的一个特殊队列-传输队列(Transmission Queue)。我们建立一条从系统A到系统B的消息通道,消息通道代理将从传输队列中读取消息,并传递这条信息到系统B,然后等待确认。只有MQ接到系统 B成功收到信息的确认之后,它才从传输队列中真正将该信息删除。如果通讯线路不通,或系统B不在运行,信息会留在传输队列中,直到被成功地传送到目的地。这是MQ最基本而最重要的技术--确保信息传输,并且是一次且仅一次(once-and-only-once)的传递。

  MQ提供了用于应用集成的松耦合的连接方法,因为共享信息的应用不需要知道彼此物理位置(网络地址);不需要知道彼此间怎样建立通信;不需要同时处于运行状态;不需要在同样的操作系统或网络环境下运行。

  MQ的基本配置举例

  在上图中,要实现网络上两台主机上的通讯,若采用点对点的通讯方式,我们至少要建立如下MQ的对象:

  在发送方A:

  1) 建立队列管理器QMA: crtmqm -q QMA

  2) 定义本地传输队列: define qlocal (QMB) usage (xmitq) defpsist(yes)

  3) 创建远程队列: define qremote (QR.TOB) rname (LQB) rqmname (QMB) xmitq (QMB)

  4) 定义发送通道: define channel (A.TO.B) chltype (sdr) conname ('IP of B') xmitq (QMB) + trptype (tcp)

  在接收方B:

  1) 建立队列管理器QMB: crtmqm -q QMB

  2) 定义本地队列QLB: define qlocal (LQB)

  3) 创建接收通道: define channel (A.TO.B) chltype (rcvr) trptype (tcp)

  经过上述配置,我们就可以实现从主机A到B的单向通讯,若要实现二者之间的双向通讯,可参考此例创建所需要的MQ对象。

  MQ的通讯模式

  1) 点对点通讯:点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。

  2) 多点广播:MQ适用于不同类型的应用。其中重要的,也是正在发展中的是"多点广播"应用,即能够将消息发送到多个目标站点(Destination List)。可以使用一条MQ指令将单一消息发送到多个目标站点,并确保为每一站点可靠地提供信息。MQ不仅提供了多点广播的功能,而且还拥有智能消息分发功能,在将一条消息发送到同一系统上的多个用户时,MQ将消息的一个复制版本和该系统上接收者的名单发送到目标MQ系统。目标MQ系统在本地复制这些消息,并将它们发送到名单上的队列,从而尽可能减少网络的传输量。

  3) 发布/订阅(Publish/Subscribe)模式:发布/订阅功能使消息的分发可以突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息。发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发。在MQ家族产品中,MQ Event Broker是专门用于使用发布/订阅技术进行数据通讯的产品,它支持基于队列和直接基于TCP/IP两种方式的发布和订阅。

  4) 群集(Cluster):为了简化点对点通讯模式中的系统配置,MQ提供Cluster(群集)的解决方案。群集类似于一个域(Domain),群集内部的队列管理器之间通讯时,不需要两两之间建立消息通道,而是采用群集(Cluster)通道与其它成员通讯,从而大大简化了系统配置。此外,群集中的队列管理器之间能够自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器可以接管它的工作,从而大大提高系统的高可靠性。

  MQ Server和MQ Client

  MQ产品分为Server和Client 两种版本,在MQ服务器的运行环境下,有队列管理器、队列、消息通道等对象,它提供全面的消息服务;MQ Client为我们提供了一个MQ应用程序的开发和运行环境,它是MQ API的Client实现。在客户端环境下,没有队列管理器、队列等对象,它通过MQI通道与服务器之间建立通讯,并将消息从客户端发往服务器端的队列,或从Server端的队列中取得消息,它比较适合于网络条件较好或实时通讯的情况。同时要指出的是:采用MQ Client并不会导致数据的丢失或不完整性。MQ Client提供下列好处:适合同步处理的工作模式;减少系统负担;减少系统管理开销;减少磁盘空间要求等。

  MQ的API

  MQ支持多种编程语言,其中包括:C、C++、Java、VisualBasic、COBOL、PL/1、RPG等,同时也支持多种流行的开发工具,如:Websphere Studio Application Developer, PowerBuiler、Microsoft Visual C++、Visual Basic、Delphi等。并且,MQ在不同平台上提供统一的编程接口,仅需重新编译就可完成不同平台间程序的移植。MQ的API接口十分简单易学,用户仅需利用MQ的13个常用而又功能强大的函数调用,便可以以最快的速度,写出各种复杂的应用程序。用户可以将主要精力集中于应用业务逻辑的实现,而不是底层通讯、例外处理等方面。

  以C语言为例,一个MQ应用的开发流程如下:

  

  MQCONN() /*建立与队列管理器的连接*/

  MQOPEN() /*打开要进行读写操作的队列*/

  MQPUT() /*将消息放入队列*/

  MQGET() /*从队列中读取消息*/

  MQINQ() /*查询队列的属性*/

  MQSET() /*设置队列的属性*/

  MQCLOSE() /*在读写等操作进行完之后,将队列关闭*/

  MQDISC() /*断开与队列管理器的连接,释放相关的资源*/

IBM MQSeries使用指南

更多精彩请到 http://www.139ya.com

转自 : http://download.zdnet.com.cn/software_zone/2007/1005/536606.shtml



随着计算机网络和分布式应用的不断发展,远程消息传递越来越成为应用系统中不可缺少的组成部分。商业消息中间件的出现保证了消息传输的可*性,高效率和安全性,同时也减少了系统的开发周期。目前应用最多的消息中间件产品为IBM MQSeries。本文就针对MQ的基本操作与配置进行详细的阐述,希望对读者有所帮助。


一.MQ基本操作



MQ中有几个很重要的组件:队列管理器(QueueManager)、队列(Queue)和通道(Channel)。其基本的操作方法如下:



创建队列管理器
crtmqm –q QMgrName
-q是指创建缺省的队列管理器



删除队列管理器
dltmqm QmgrName



启动队列管理器
strmqm QmgrName
如果是启动默认的队列管理器,可以不带其名字



停止队列管理器
endmqm QmgrName 受控停止



endmqm –i QmgrName 立即停止



endmqm –p QmgrName 强制停止



显示队列管理器
dspmq –m QmgrName



运行MQSeries命令
runmqsc QmgrName
如果是默认队列管理器,可以不带其名字



往队列中放消息
amqsput QName QmgrName
如果队列是默认队列管理器中的队列,可以不带其队列管理器的名字



从队列中取出消息
amqsget QName QmgrName
如果队列是默认队列管理器中的队列,可以不带其队列管理器的名字



启动通道
runmqchl –c ChlName –m QmgrName



启动侦听
runmqlsr –t TYPE –p PORT –m QMgrName



停止侦听
endmqlsr -m QmgrName



MQSeries命令



定义死信队列
DEFINE QLOCAL(QNAME) DEFPSIST(YES) REPLACE



设定队列管理器的死信队列
ALTER QMGR DEADQ(QNAME)



定义本地队列
DEFINE QL(QNAME) REPLACE



定义别名队列
DEFINE QALIAS(QALIASNAME) TARGQ(QNAME)



远程队列定义
DEFINE QREMOTE(QRNAME) +
RNAME(AAA) RQMNAME(QMGRNAME) +
XMITQ(QTNAME)




定义模型队列
DEFINE QMODEL(QNAME) DEFTYPE(TEMPDYN)



定义本地传输队列
DEFINE QLOCAL(QTNAME) USAGE(XMITQ) DEFPSIST(YES) +
INITQ(SYSTEM.CHANNEL.INITQ)+
PROCESS(PROCESSNAME) REPLACE



创建进程定义
DEFINE PROCESS(PRONAME) +
DESCR(‘STRING’)+
APPLTYPE(WINDOWSNT)+
APPLICID(’ runmqchl -c SDR_TEST -m QM_ TEST’)
其中APPLTYPE的值可以是:CICS、UNIX、WINDOWS、WINDOWSNT等



创建发送方通道
DEFINE CHANNEL(SDRNAME) CHLTYPE(SDR)+
CONNAME(‘100.100.100.215(1418)’) XMITQ(QTNAME) REPLACE
其中CHLTYPE可以是:SDR、SVR、RCVR、RQSTR、CLNTCONN、SVRCONN、CLUSSDR和CLUSRCVR。



创建接收方通道
DEFINE CHANNEL(SDR_ TEST) CHLTYPE(RCVR) REPLACE



创建服务器连接通道
DEFINE CHANNEL(SVRCONNNAME) CHLTYPE(SVRCONN) REPLACE



显示队列的所有属性
DISPLAY QUEUE(QNAME) [ALL]



显示队列的所选属性
DISPLAY QUEUE(QNAME) DESCR GET PUT
DISPLAY QUEUE(QNAME)MAXDEPTH CURDEPTH



显示队列管理器的所有属性
DISPLAY QMGR [ALL]



显示进程定义
DISPLAY PROCESS(PRONAME)



更改属性
ALTER QMGR DESCR(‘NEW DESCRIPTION’)
ALTER QLOCAL(QNAME) PUT(DISABLED)
ALTER QALIAS(QNAME) TARGQ(TARGQNAME)



删除队列
DELETE QLOCAL(QNAME)
DELETE QREMOTE(QRNAME)



清除队列中的所有消息
CLEAR QLOCAL(QNAME)

二.配置一个能够通信的远程连接
以上讲述了MQ的基本命令操作,但只知道这些是没有实际意义的。MQ的最终目的是实现远程通信,所以下面就以一个具体的例子来说明如何实现远程连接。这个例子的目的是建立可以实现消息传递的一对MQ服务器,它们分别基于NT和UNIX平台。
首先在NT端建一队列管理器
crtmqm –q QM_NT
启动队列管理器
strmqm QM_NT
运行MQ控制台命令
runmqsc QM_NT
创建死信队列
DEFINE QL(NT.DEADQ) DEFPSIST(YES) REPLACE
更改队列管理器属性,设置其死信队列
ALTER QMGR DEADQ(NT.DEADQ)
创建进程定义
DEFINE PROCESS(P_NT)+
APPLTYPE(WINDOWSNT)+
APPLICID(’ runmqchl -c SDR_NT -m QM_NT’)
创建本地传输队列
DEFINE QL(QT_NT) USAGE(XMITQ) DEFPSIST(YES) +
INITQ(SYSTEM.CHANNEL.INITQ)+
PROCESS(P_NT) REPLACE
创建远程队列定义,对应于UNIX机器上的本地队列Q_UNIX,传输队列为QT_NT
DEFINE QREMOTE(QR_NT)+
RNAME(Q_UNIX) RQMNAME(QM_UNIX)+
XMITQ(QT_NT)
创建发送方通道,其传输队列为QT_NT,远程主机地址为10.10.10.2,侦听端口为1414
DEFINE CHANNEL(SDR_NT) CHLTYPE(SDR)+
CONNAME(‘10.10.10.2(1414)’) XMITQ(QT_NT) REPLACE
创建服务器连接通道
DEFINE CHANNEL(S_NT) CHLTYPE(SVRCONN) REPLACE

在UNIX端创建队列管理器
crtmqm –q QM_UNIX
启动队列管理器
strmqm QM_UNIX
添加侦听程序
修改/etc/services文件,加入一行:
MQSeries 1414/tcp #MQSeries channel listener
修改/etc/inetd.conf文件,加入一行(启动侦听程序)
MQSeries stream tcp nowait mqm /usr/lpp/mqm/bin/amqcrsta amqcrsta –m QM_UNIX
运行以下命令,以使修改起作用
refresh –s inetd

运行MQ控制台命令
runmqsc QM_UNIX
创建死信队列
DEFINE QL(UNIX.DEADQ) DEFPSIST(YES) REPLACE
更改队列管理器属性,设置其死信队列
ALTER QMGR DEADQ(UNIX.DEADQ)
创建接收方通道,其名字必须与远程发送方相同
DEFINE CHANNEL(SDR_NT) CHLTYPE(RCVR) REPLACE
创建本地队列
DEFINE QL(Q_UNIX) DEFPSIST(YES) REPLACE
创建服务器连接通道
DEFINE CHANNEL(S_UNIX) CHLTYPE(SVRCONN) REPLACE

经过以上操作之后,远程连接的配置工作完成。接下来需要验证配置是否正确。
在NT端启动发送方通道
runmqchl –c SDR_NT –m QM_NT 或 start chl(SDR_NT)
从NT端发送消息到UNIX端
amqsput QR_NT QM_NT
在UNIX端接收消息
/usr/mqm/samp/bin/amqsget Q_UNIX QM_UNIX

若能收到消息,说明配置成功。

三.配置JNDI
用JMS实现消息的发送和接收时,经常会用到JNDI。因为JNDI这种方式比较灵活,对于编程也比较简单。
在安装了MQSeries Client for Java之后,在javabin目录下找到JMSAdmin.config文件。该文件主要用来说明Context的存储方式及存储地址,对应于文件中的两个参数INITIAL_CONTEXT_FACTORY和PROVIDER_URL。典型的JMSAdmin.config文件内容如下:

#INITIAL_CONTEXT_FACTORY=com.sun.jndi.ldap.LdapCtxFactory
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
#INITIAL_CONTEXT_FACTORY=com.IBM.ejs.ns.jndi.CNInitialContextFactory
#
#PROVIDER_URL=ldap://polaris/o=IBM,c=us
PROVIDER_URL=file:/d:/temp
#PROVIDER_URL=iiop://localhost/
#
SECURITY_AUTHENTICATION=none

INITIAL_CONTEXT_FACTORY 表示JMSAdmin Tool使用的服务提供商。当前有三种受支持的值。com.sun.jndi.ldap.LdapCtxFactory用于LDAP,如果使用它就必须安装一个LDAP服务器。com.sun.jndi.fscontext.RefFSContextFactory用于文件系统上下文,它只需要使用者提供存放上下文的文件路径。com.IBM.ejs.ns.jndi.CNInitialContextFactory是专门为websphere提供的,它需要和websphere的CosNaming资源库一起使用。
PROVIDER_URL表示会话初始上下文的URL,由JMSAdmin tool实现的所有JNDI操作的根。它和INITIAL_CONTEXT_FACTORY一一对应。

ldap://hostname/contextname 用于LDAP
file:[drive:]/pathname 用于文件系统上下文
iiop://hostname[:port]/[?TargetContext=ctx] 用于访问websphere CosNaming名称空间

最后还有一个参数SECURITY_AUTHENTICATION,用于说明JNDI是否把安全性凭证传递给了您使用的服务供应商。只有当使用了LDAP服务供应商时,才使用此参数。此参数有三个值,none(匿名认证)、simple(简单认证)和CRAM-MD5认证机制。如果没有提供有效值,缺省值为none。

确认配置文件之后,可以在javabin目录下启动JMSAdmin控制台。也可以在任何目录下用下面的命令来启动控制台:
JMSAdmin –cfg MQ_JAVA_INSTALL_PATHjavabinJMSAdmin.config
其中MQ_JAVA_INSTALL_PATH为MQSeries Client for Java安装的根目录。
若启动失败,则好好检查一下您的环境变量是否设置正确。根据我个人的经验,除了把com.IBM.mq.jar和com.IBM.mqjms.jar加入到环境变量外,还要把fscontext.jar和providerutil.jar加入到环境变量。
进入JMSAdmin控制台后,您可以自由定义sub context。对于子上下文的操作,主要有一下命令:
display ctx
define ctx(ctxname)
change ctx(ctxname)
change ctx(=up)
change ctx(=init)
delete ctx(ctxname)

当然,在这里的主要任务并非是用来定义sub context,而是用来定义以下几个对象:
MQQueueConnectionFactory
MQTopicConnectionFactory
MQQueue
MQTopic
(还有其它的一些对象,如MQXAQueueConnectionFactory等,不常用到,在此不作说明。)


可以使用很多动词来操纵目录名称空间中的受管理对象。ALTER、DEFINE、DISPLAY、DELETE、COPY和MOVE,它们的用法都算比较简单,这里只列举一二以作说明。
例一:定义一QueueConnectionFactory,连接主机10.10.10.18,端口1414
DEFINE QCF(EXAMPLEQCF)+
DESC(Example Queue Connection Factory)+
TRAN(CLIENT)+
HOST(10.10.10.1+
QMGR(QM_EXAMPLE)+
CHAN(S_EXAMPLE)+
PORT(1414)+
CCSID(1381)

例二:定义一Queue,其对应于MQ中的Q_EXAMPLE
DEFINE Q(EXAMPLEQL)+
DESC(Local queue)+
QMGR(QM_EXAMPLE)+
QUEUE(Q_EXAMPLE)+
CCSID(1381)

四.用JMS实现MQ编程
上面我们说明了怎样用JMSAdmin Tool定义MQ对象的上下文。我们的最终目的是要用JMS来实现MQ编程,以实现在程序中对MQ队列进行收、发消息。所以,下面我们将重点讨论一下MQ的JMS实现。
如果您对JMS编程很熟悉,那么您也就会用JMS来实现MQ编程,因为用JMS来编写MQ程序与编写一般的JMS程序没有太大的差别。举个例子,当我们想发送一条消息到MQ的队列中,再从该队列中取回消息时,我们编程时主要有四个步骤。首先我们要初始化在程序中要用到的对象,然后才可以发送消息到队列中去,再就是收取消息了,最后要清除那些永久对象。这些都和普通的JMS程序相当。程序的源代码如下:

代码:

import java.util.Hashtable;
import javax.jms.*;
import javax.naming.*;
import javax.naming.directory.*;

public class sample {
protected QueueConnectionFactory factory=null;
protected QueueConnection connection;
protected QueueSession queueSession;
protected TextMessage outMessage;
protected QueueSender queueSender;
protected QueueReceiver queueReceiver;

public static final String qcfLookup="EXAMPLEQCF";
public static final String qLookup="EXAMPLEQL";
public static final String icf = "com.sun.jndi.fscontext.RefFSContextFactory";
public String url ="file:/d:/temp";

public void sampleInit() throws Exception {
Hashtable environment = new Hashtable();
environment.put(Context.INITIAL_CONTEXT_FACTORY, icf);
environment.put(Context.PROVIDER_URL, url);
environment.put(Context.REFERRAL, "throw");
Context ctx=new InitialDirContext(environment);
factory = (QueueConnectionFactory)ctx.lookup(qcfLookup);
Queue q1=null;
q1=(Queue)ctx.lookup(qLookup);
connection = factory.createQueueConnection();
queueSession = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

queueSender = queueSession.createSender(q1);
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
outMessage = queueSession.createTextMessage();
queueReceiver = queueSession.createReceiver(q1);
connection.start();
}
public void sendMessageOut(String message) throws JMSException {
outMessage.setText(message);
queueSender.send(outMessage);
}
public String receiveMessage() throws Exception{
return ((TextMessage)queueReceiver.receive()).getText();
}
public void sampleClose() throws JMSException {
queueSession.close();
connection.close();
}

public static void main(String[] args){
String rec;
sample sp = new sample();
try {
sp.sampleInit();
sp.sendMessageOut("Hello World!");
java.lang.Thread.sleep(4000);
rec=sp.receiveMessage();
System.out.println("Receive text is : "+rec);
sp.sampleClose();
}catch(Exception e) {
e.printStackTrace();
}
}
}

五.远程管理
MQ在WINDOWS平台下具有图形化管理界面,但在UNIX平台下却只能通过命令行来进行操作。这样就给使用者带来很大的不便。我们都希望能通过图形界面来进行管理配置。为了实现我们的想法,我们就必须建立远程管理。
实现远程管理有以下几个步骤:
1.被管理队列管理器上的命令队列SYSTEM.ADMIN.COMMAND.QUEUE存在并可用。对于MQ 2版本应执行 amqscoma.tst 脚本来创建。
2.使用strmqcsv命令来启动被管理队列管理器上的命令服务器。
3.确定被管理队列管理器上的服务器连接通道SYSTEM.ADMIN.SVRCONN是否存在,如果不存在则创建它。
4.一般Unix、Linux平台中MQ默认的字符集为819,而Windows平台为1381,所以你必须改变其字符集,使两边的字符集相同。一般改被管理的字符集。
5.如果被管理队列管理器上的操作用户与管理队列管理器上的操作用户不同,那么你首先要确认管理队列管理器上的操作用户在被管理队列管理器上存在并且有管理 MQ的权限,再者,你需要修改服务器连接通道SYSTEM.ADMIN.SVRCONN的MCAUSER属性为管理队列管理器上的操作用户。
6.启动被管理队列管理器上的侦听器。

做完这些工作之后,直接在管理队列管理器的MQ管理工具中显示被管理队列管理器即可。然后你就可以象操作本地队列管理器一样,在被管理队列管理器上定义你需要的MQ对象。


六.通道维护
在配置远程连接的时候,我们曾经创建过进程定义。那我们为什么要去创建进程定义呢?这就涉及MQ通道维护的概念。
通道长时间没有消息触发就会自动断开连接,不再保持运行状态。时间的长短可以由自己设定,默认值为6000秒。消息请求再次来临的时候,就必须再次启动通道。有些通道,如服务器连接通道、接收方通道等是自动触发启动的。当消息请求发送到通道后,通道立即启动,进入运行状态。但也有一些通道不会自动启动,最典型的就是发送方通道。当有消息请求需要使用通道进行消息传递的时候,发送方通道也不会自动启动并把消息发送到远程队列,而是把消息留在了与其相关联的传输队列中。
但是,在实际应用中我们又不可能每过一段时间去启动一次通道,或当有消息来再去启动通道。那应该怎么办?首先我们创建一个进程定义,这个进程定义的目的就是用来启动发送方通道。然后我们在传输队列的进程名称属性栏指定刚才定义的进程定义名称,再把触发器控制开关打开。这样,当有消息进入传输队列后,传输队列的触发器会启动触发执行指定的进程,从而启动发送方通道,把消息传输到远程队列中去。

七.总结
也许你会说你已经理解了MQ,包括基本概念、配置、管理以及程序的编写,那么恭喜你。我也希望如此。不过MQ也许并非如此简单,因本人水平有限,也只能到此为止,希望大家再接再厉。顺便说一句,如果有什么好想法,千万别忘了告诉我一声。

Wednesday, January 7, 2009

MQ配置和编程最佳实践

更多精彩请到 http://www.139ya.com


对于MQ的使用,主要会涉及到MQ系统本身的配置和MQ应用程序的开发两方面的工作。为了帮助大家更好地使用MQ,本文将就MQ配置和编程中的一些注意事项和技巧与大家探讨,并希望与大家分享这方面的一些最佳实践(Best Practice)。

第一部分:有关MQ对象配置的最佳实践

对于MQ系统配置,我们要规划MQ通讯网络,确定系统的拓扑结构,确定各种对象的属性和命名规则并创建所需的各种对象等,首先,我们谈一谈在系统建设之初,如何设计和定义MQ的各种对象。

1、有关队列管理器:

创建队列管理器时,应考虑的因素主要有:

1) 队列管理器的日志类型以及日志文件的大小和个数,要根据用户数据量的大小、各个队列上的消息总容量,来计算日志的总容量,以免在系统运行过程中出现日志写满的情况;

2) 应该为队列管理器指定和建立死信队列;

3)对最多打开句柄数MAXHANDS(缺省为256,如果您需要多于256个应用程序同时连接队列管理器,应增大该值),最大消息长度 MAXMSGL,最多的未提交的消息个数MAXUMSGS属性(缺省为10000,如果您使用了消息分段或分组,某个大消息的分段个数超过了10000,应增大该值)的考虑;

4) 创建完队列管理器之后,应修改队列管理器的配置文件,考虑有关TCP和通道有关的参数的配置,举例如下:



TCP:

KeepAlive=Yes

Channels:

AdoptNewMCA=ALL

PipeLineLength=2

MaxActiveChannels=200



2、有关队列:

对于队列的属性,应该考虑的因素主要有:

1) 永久性和非永久性设置:尤其要注意的是DEFPSIST属性的缺省值为No,若要保证消息的安全可靠,必须将其设置为Yes;

2) 对于本地队列和传输队列,要考虑队列的最大深度MAXDEPTH(缺省为5000,应根据实际情况计算该值),队列中每个消息的最大字节数MAXMSGL的配置。

3、有关通道:

对于通道的属性,应该考虑的因素主要有:

1) 确定通道的运行方式采用长连接的方式还是触发的方式,通常,对于需要频繁启动的通道,不适宜采用触发的方式。若采用触发方式启动通道,触发类型应为FIRST;

2) 对于发送类型的通道,要考虑通道的断开间隔(DISCINT)、短重试次数(SHORTRTY)、短重试间隔(SHORTTMR)、长重试次数(LONGRTY)、长重试间隔(LONGTMR)、批处理大小(BATCHSZ)的配置。
第二部分:有关MQ程序开发的最佳实践

通常大家在使用MQ时,一般在系统设计之初只考虑MQ的系统配置,而很少提前考虑应用程序编写的指导原则,往往是边写边想,边想边写,边写边改,使得开发效率和质量都比较低,为了使大家更快、更好地开发出MQ应用程序,在该部分我们给出与MQ程序开发相关的一些最佳实践,供大家参考。这里我们无法做到面面俱到,并且由于每个用户的具体需求不同,每条原则都不能一概而论,但是从普遍意义上可以作为您的参考。

为了更好地掌握和了解MQ编程的技巧,我们首先要熟悉MQ的消息通讯模式,这将帮助你更好地理解MQ编程的最佳实践。通常,MQ有两种通讯模式,即数据报 (Datagram) 方式和请求/应答(Request/Reply) 方式:其中,Datagram方式通常又被称为"Send And Forget"(发送/忽略),是最简单的通讯模式,应用程序只需在创建完消息之后,利用MQ的API将消息发送到队列中,它充分利用了MQ确保消息传输,并且传一次且仅传一次(once and once only)的优势,发送端应用程序无需关心消息何时被处理。

Request/Reply(请求/应答)方式相对复杂一些,在消息发出之后,你需要等待对方的处理结果,在这种情况下,你通常需要考虑其他一些问题,如:

等待应答的时间是多少?

如果没有收到应答,是否再次发出请求?

应答发出之前是否会有数据库操作或其他交易被执行?

本次请求/应答过程的会话(session)信息是否需要被保留?

通常,我们要根据用户的需求来决定采用何种通讯模式,不同的通讯模式之下对应用程序的考虑将会有所不同,使用的MQ API的参数和选项也将不同。

为了使你的MQ应用能够更加健壮,并且具有更强的可维护性,我们要学会灵活高效地使用MQ的一些特性以及相关的API选项,从而提到应用程序的质量、灵活性、可靠性和性能。这里,我们将给出一些较典型的建议。

1. 在每一个MQ API调用之后,必须检查完成码(completion code)和原因码(reason code),对于非零的返回码,必须进行相应的处理,必要时,最好将返回码记录错误日志,从而在应用程序出现运行故障时便于检查和处理。

在MQ 中,所有的API调用都会得到其完成码和原因码,完成码有MQCC_OK、MQCC_WARNING、MQCC_FAILED等,MQCC_OK表示调用成功,当得到MQCC_WARNING、MQCC_FAILED返回码时,表示API执行不完全成功,你需要进一步检查原因码,通过不同的原因码分析失败原因。如:原因码2033代表队列已空,没有消息可取;2080代表消息的实际长度超过了你在程序代码中设置的缓冲区长度等等。

2. 对MQCONN, MQOPEN,MQCLOSE, MQDISC的使用

由于MQCONN, MQOPEN,MQCLOSE, MQDISC相对于MQGET和MQPUT来说是比较消耗资源的几个函数,在一个应用中,即使你需要对某个队列进行多次读写操作,也不要对每一次读写都调用一次MQCONN, MQOPEN,MQCLOSE, MQDISC函数,正确的做法应该是,调用一次MQCONN, MQOPEN就可以对队列进行多次读写操作,另外,一定别忘记对称地使用MQCLOSE, MQDISC函数将相关资源释放掉。

3. 读取消息时,等待时间间隔的设置

对队列的读取操作,可以有两种方式,即轮巡方式和触发方式(利用MQ的触发功能动态调起应用程序)。在采用轮巡方式读取队列时,在MQGET时,我们需要设置消息读取选项MQGMO_WAIT,同时指定等待时间间隔。不少用户将时间间隔设定为MQWI_UNLIMITED以实现轮巡的目的,这样做的弊端在于在没有消息到达时应用程序陷入无限的等待,无法接收来自外部系统的相关信号,MQCLOSE,MQDISC调用也无法被执行,因此,我们建议避免采用这种方式,推荐的做法是设置特定的等待时间间隔,然后再循环发出MQGET调用。

4. 如果在同步点控制之下使用MQGET,在所有MQGET调用之后,必须检查消息的回滚次数(Backout Count)。

如果某个消息是在同步点控制之下读取的,并且由于某种原因消息被回滚,消息描述符中(Message Descriptor)的BackoutCount字段的值将被加1,你需要判断该数值,如果它大于某个阈值,你需要使用其它手段来处理该消息。否则,在某些情况下会导致读取消息-消息被回滚-再读取消息-消息再被回滚的死循环。

例如:你使用了触发机制设定当队列中消息到达时,触发某个应用程序,该应用程序在MQ XA Resource Manager的控制之下,读取消息,并且利用其数据对数据库进行更新,假设数据库出现问题,无法成功进行数据库操作,消息将被回滚;这时,又满足了触发条件,又会触发起该应用程序,周而复始,陷入死循环。在这种情况下,你必须在程序中加入对BackoutCount的判断。

5. 当处理backout消息时,可以使用队列的BOTHRESH 和 BOQNAME属性。

如4 中所言,如果某个消息是在同步点控制之下读取的,并且由于某种原因消息被回滚,消息描述符中的BackoutCount字段的值将被加1,你需要判断该数值,如果它大于某个阈值,你需要使用其它手段来处理该消息。在处理该消息的应用中,你可以将其与设定的阈值做比较,这时,阈值会被写死在程序中,为了提高其灵活性,你可以使用队列的BOTHRESH 和 BOQNAME属性。这样,你可以在例外处理中,利用MQINQ查询得到阈值的大小,如果超出,可以将消息转发到BOQNAME指定的队列中,继而对该队列进行相应的处理。这种方法大大增强了应用程序的灵活性。

6. 在使用MQOPEN, MQPUT 和 MQGET调用时,要使用FAIL_IF_ QUIESCING的选项。

MQ 系统本身和使用它提供的服务的用户应用程序之间是互相独立的,必要时,我们可能要停止MQ系统,这时,我们不但希望新的应用不能连接,并且希望所有已连接的应用能够立即停止。为了使所有的应用程序能够快速得知MQ系统正在停止的信号,在上述MQ API中,必须设置FAIL_IF_ QUIESCING的选项。

如果不设置FAIL_IF_ QUIESCING的选项,当MQ系统停止时,所有应用将继续运行,这样会影响MQ系统的停止,从而导致MQ停止需要很长时间,同时可能导致我们必须手工杀掉那些没有设置该选项的应用程序。

7. 消息描述符的不同字段的使用方法

在MQ 的消息描述符MQMD中包含了很多字段,这些字段大部分是一些保留字段,例如:MsgID表示消息的唯一标识,如果你不指定,MQ系统会为你自动产生一个,并保证其唯一性;PutAppType, PutAppName, PutDate, PutTime是系统自动产生的,表示哪个应用何时将消息发送到队列中;再如:GroupId, MsgSeqNumber, Offset, MsgFlags是与消息分段和消息分组相关的控制信息;除了这些系统自动产生无法更改的字段和有特殊用途的字段之外,如果您想选择某些字段为己所用,将其设定为自己应用程序中某个有意义的标识,你可以使用CorrelId和Feedback字段,但是,按照惯例,CorrelId常常被用于在请求/应答通讯模式中来表示请求消息和应答消息之间的关联;因此,我们可以灵活使用Feedback字段,利用该字段来进行一些应用程序控制。当我们接收到某个消息之后,我们可以检查Feedback字段,根据Feedback字段值进行相应的处理。

8. 消息永久性属性的确定

永久性消息保证了消息在系统和网络等故障下的安全可靠,但是同时从性能角度来讲会比非永久性消息要差,因此,要从不同的角度进行权衡和分析,然后决定消息的永久性属性。当对性能要求非常高,可靠性要求相对不高时,可以首先考虑采用非永久性消息,在消息决不允许有任何丢失,并且在丢失之后又无法重新发送时,要使用永久性消息。

9. 当指定消息的永久性和非永久性属性时,最好利用应用程序显式地指定,不要使用"defined as queue"的方法来指定。

消息的永久性和非永久性是消息本身的属性,多数情况下,只有消息的原始发出者才了解丢失消息将产生的重大影响,因此,消息的原始发出者应在应用程序中显式地指定消息的永久性,如果将其定义为依赖于队列的该属性,就会比较被动,当队列的永久性属性(DEPSIST)被意外地设为NO时,就会有丢失的风险。

10. 应用程序可以将请求消息的永久性属性为No,即对于请求消息使用非永久性消息。

一般情况下,请求消息丢失对应用系统不会产生严重的影响,如果出现请求消息丢失的情况,我们可以重新发送,因此,不必将请求消息设置为永久性消息。把请求消息设置为非永久性消息的另外一个好处是,系统不需要对非永久性消息记录日志,从而减少I/O操作,提高系统的性能。

11. 在异种操作系统平台上使用MQ传输消息时,将消息格式设置为MQFMT_STRING。

MQ 的一大优势之一,是对built-in(内置的)消息格式,可以实现不同操作系统平台间、不同系统字符集之间的数据转换,如开发平台ASCII码和主机 EBCDIC码之间的转换。为了实现该数据转换,MQ必须获知本身和对方MQ系统的队列管理器的CCSID和Encoding以及消息的格式。一般而言, CCSID和Encoding会被自动设置和处理,不需要应用程序关心,但是,消息的格式必须由应用程序指定,对于MQ内置支持的消息格式,MQ可以自动转换,这些消息格式由MQFMT_*来指定。鉴于应用程序数据都可以用MQFMT_STRING来表示,并且MQFMT_STRING是MQ内置支持可以转换的格式之一,你可以使用它来表示你的消息格式,同时,对于数字型消息,你需要使用atoi, itoa等函数实现数字型和字符型之间的转换。

12. 对大消息的处理

MQ 支持单条消息的最大长度为100M,队列管理器、队列、通道支持的最大消息的缺省值为4M,即使如此,我们却应该根据不同的网络类型和带宽,具体地确定不同情况下单条消息的合适大小。例如:如果在拨号网络或网络带宽较窄的情况下,我们将单条消息的大小设置得太大,就会影响传输效率,在这种情况下,一定要使用MQ的分段功能将消息进行分段处理,确保每一个物理消息的大小适当,MQ会自动维护整个逻辑消息的完整性,并且可以在接收端一次性将其取出。

13. 如何使用MQ的请求/应答通讯模式来处理同步的消息处理模式。

大家知道,MQ的异步处理模式是非常强健的,同时它也支持同步的消息处理,例如MQ的client-server通讯就是一种典型的同步工作模式,对于 server-server通讯,我们也可以实现同步工作模式,这里就涉及到如何巧妙地使用MQ提供的消息生命周期的功能。这时,对于请求消息和应答消息我们最好为其设置生命周期。

典型的应用案例如下:假设系统A向系统B发出请求,调用B上的某个交易,这里,我们首先要设置请求消息的生命周期,并且在消息到期时将消息丢弃,如果在消息发出之前消息过期,它就会在进入通道之前,被MQ系统丢弃;如果当消息到达目的地之后,在被对方应用程序取走之前消息过期,它也将被MQ系统丢弃。系统B上的交易便不会被调起执行。对于应答消息,我们也设置它的生命周期,与请求消息不同的是,我们设置在消息到期时将其转发到另外一个特定的队列中,这时,如果系统B上的交易执行完之后,会产生应答消息,如果由于通讯等原因,该应答消息在到达系统A时应用程序设置的Timeout时间已经超出,应用程序必然认为系统B上的交易没有被执行,也不会处理该应答消息,这样,应答消息便会过期,当它过期时,根据我们的设置,它会自动被MQ系统转发到特定队列中,我们另设专门的应用程序对此进行冲正处理。

14. 对消息类型的设置。

通常情况下,我们不要求用户一定去设置消息的类型,设置消息类型的方法是在消息描述符MQMD的MsgType字段,消息类型有 datagram, request, reply, report等若干种。但是为了更好地对消息进行管理,我们必要时要设定消息类型,从而对不同的消息进行处理。例如,当reply消息和report消息都被发送到同一个应答队列时,我们可以根据消息类型的不同对其进行不同的处理。

15. 对消息转换的设置。

通常,当需要进行消息转换时,我们有两种方法来进行设置:一种方法是在调用MQGET时设置MQGMO_CONVERT的读取消息选项;第二种方法是将发送端通道的convert属性设置为yes。对比二者,我们推荐使用第一种方法,它的优势在于:它避免了消息通道代理程序对消息的转换,从而提高了通道的性能;其次,当一对多传输时,避免了对不同的目标系统使用不同的转换算法,而统一放在接收端进行。

当我们不需要进行格式转换时,使用在MQMD中将消息格式Format字段设置为MQFMT_NONE的方法来表示,MQFMT_NONE表示忽视任何格式转换。

16. 应用程序最好不要在MQGET和MQPUT调用时使用过大的消息缓冲区,从而减少队列管理器对内存的需求。

当应用程序发出MQGET和MQPUT调用时要设定用于装载消息的数据缓冲区大小,MQ系统将据此来分配内存,如果使用过大的消息缓冲区,队列管理器就会分配较大的内存来处理这些调用,从而造成内存浪费,影响性能。如果我们在接收端不知道消息的大小,为了不至于设置一个很大的缓冲区去接收数据,我们可以在真正的MQGET之前先使用Browse方式来浏览一下队列中的消息,根据浏览到的消息的MQMD的OriginalLength字段的数值来确定消息缓冲区的大小,然后再使用MQGET真正的将消息读取出来。

17. 在设定消息的优先级时,不要直接在消息描述符中设置,最好设置队列的优先级,然后使用"defined as queue"来设置消息的优先级。

优先级是消息的属性之一,当应用发生变化时,我们可能需要改变消息的优先级,如果我们将其写死在程序中,就会影响程序的灵活性。如果借助于队列的优先级来设置消息的优先级,系统管理员可以根据需要,通过改变队列的DEFPRTY的属性来更改其优先级,而无需改变应用程序代码,这样可以大大提高系统管理和网络管理的灵活性。

18. 请求型应用在打开请求队列时,最好不要使用MQOO_OUTPUT选项。

请求队列可以是本地队列,也可能是远程队列,本地队列是既可读又可写的,而远程队列是只可写(MQOO_OUTPUT)的。通常,请求型应用一般都是需要将请求发送到请求队列中,对队列的操作都是MQPUT,这时,队列的类型不会有影响,因为本地队列和远程队列都是可写的。但是,如果某个请求型应用需要读取请求队列,则要求请求队列一定是本地队列,这时就不能使用MQOO_OUTPUT选项。因此建议在打开请求队列时,最好不使用 MQOO_OUTPUT选项,可以使得不同的应用之间很容易移植,而且当队列属性改变时,不会对应用程序造成影响。

19. 与数据库交互时,MQPUT 和 MQGET必须使用同步点控制,即使用MQ的两阶段提交功能,来保证数据的一致性和完整性。

为了保证数据库操作和MQ操作同时成功或同时回滚,需要在做MQPUT 和 MQGET调用时,使用MQPMO_SYNCPOINT和MQGMO_SYNCPOINT选项将队列操作和数据库操作作为一个事物来完成。这样,如果数据库出现问题导致操作失败时,消息可以被正确回滚;否则,会导致数据不一致的现象。

20. 与数据库操作相关的队列消息的属性最好设为永久性消息,即消息的persistence属性应设为yes,并且永不过期。

由于与数据库操作相关的消息的重要性很强,它必须被设置为永久性消息,被MQ系统记录日志,从而在队列管理器重新启动或机器重启时不会丢失。另外,在 "Send and Forget"通讯模式之下,由于这是典型的异步通讯模式,消息何时被处理是不确定的,为了防止消息超时,我们应将其生命周期设置为永不过期。

21. 对消息做了修改或者转发的应用,最好传递原始消息的identity context(身份鉴别上下文)信息。

每个MQ的消息都有其特定的鉴别上下文,通过消息描述符(MQMD)的相关字段来表示,它代表了消息是由谁产生的,当消息在MQ网络中传输时,该上下文应该必须被保留。当消息被另外的用户修改或者转发时,需要修改其上下文,将其原始的上下文重新赋进去。这主要是处于安全的考虑,通过上下文,可以获得消息产生者的信息。

22. 保持永久性动态队列名称的唯一性,确保同一个应用多次调用/运行产生的动态队列的名字唯一性。

当应用程序需要动态产生应答队列时,它可以产生临时性动态队列和永久性动态队列,某些情况下,将利用永久性动态队列保留一些可恢复的消息,这意味着该永久性动态队列可以重新被打开,因此要保证它们名称的唯一性。

23. 在使用临时性动态队列来处理应答时,处理请求消息的应用要保证不要将应答消息设置为永久性消息。

临时性动态队列不能存储永久性消息,鉴于此,对请求做出响应的应用程序必须知道接收应答的队列是临时性动态队列还是永久动态队列,请求端应用和响应端应用必须对响应消息的永久性进行协调,以保证只有非永久性消息会被放置到临时动态队列中。

结论:为了更好地使用MQ,我们必须遵循一定的标准和指导原则,使得我们开发出更加可靠、高效的应用程序,当然,这是从普遍意义上而言,在实际应用中需要您灵活掌握,因地制宜地选用更加适合您的配置和编程模式