今天做了一个request/reply模式的MQ例子程序。
一个程序把消息放在Q上,请求的程序会在把消息放到Q上之前设置replyToQueue和replyToQueueManager消息标题属性。而后它打开回复队列并等待correlationId匹配已发出请求消息的MessageId值的消息。
另一个程序是在接受到消息后判断如果是请求消息的话,则生成回复消息并发到请求消息指定的消息队列上。它还将拷贝请求消息的MessageId到回复消息的correlationId消息标题字段上。
程序的代码如下:
import com.ibm.mq.*;/**
* @author ralph
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class Requester { public static void main(String[] args) {
try {
String hostName = "neu";
String channel = "ch_server";
String qManager = "QM_guo";
String requestQueue = "Q_request";
String replyToQueue = "Q_reply";
String replyToQueueManager = "QM_guo"; // set up the MQEnvironment properties for the client
MQEnvironment.hostname = hostName;
MQEnvironment.channel = channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID = 1381; // connetion to Q Manager
MQQueueManager qMgr = new MQQueueManager(qManager); // set up the open options
int openOptions = MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue queue = qMgr.accessQueue(requestQueue, openOptions, null,
null, null); // set the put message options,will use the default settings
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options = pmo.options + MQC.MQPMO_NEW_MSG_ID;
pmo.options = pmo.options + MQC.MQPMO_SYNCPOINT; // build a message and write data
MQMessage outMsg = new MQMessage();
outMsg.messageFlags = MQC.MQMT_REQUEST;
outMsg.replyToQueueManagerName = replyToQueueManager;
outMsg.replyToQueueName = replyToQueue; // prepare message with the user data
String msgString = "The request message from requester program!";
outMsg.writeUTF(msgString); // Now we put the message on the Q
queue.put(outMsg, pmo); // commit the transaction
qMgr.commit(); System.out.println("The message has been sussesfully put #####" ; // close the Q
queue.close(); // set up the open options
int openOptions2 = MQC.MQOO_INPUT_SHARED
| MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue respQueue = qMgr.accessQueue(replyToQueue, openOptions2,
null, null, null);
MQMessage respMessage = new MQMessage();
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;
gmo.options = gmo.options + MQC.MQGMO_WAIT;
gmo.matchOptions = MQC.MQMO_MATCH_CORREL_ID;
gmo.waitInterval = 10000;
respMessage.correlationId = outMsg.correlationId; // get the response message
respQueue.get(respMessage, gmo);
String response = respMessage.readUTF();
System.out.println("The response message is:" + response); qMgr.commit();
respQueue.close();
qMgr.disconnect();
} catch (MQException ex) {
System.out.println("Completion code is:" + ex.completionCode
+ " reason code is:" + ex.reasonCode);
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}/**
* @author ralph
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/import com.ibm.mq.*;public class Responder { public static void main(String[] args) {
try {
String hostName = "neu";
String channel = "ch_server";
String qManager = "QM_guo";
String qName = "Q_request"; // set up the MQEnvironment properties for the client
MQEnvironment.hostname = hostName;
MQEnvironment.channel = channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID = 1381; // connetion to Q Manager
MQQueueManager qMgr = new MQQueueManager(qManager); // set up the open options
int openOptions = MQC.MQOO_INPUT_SHARED
| MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue queue = qMgr.accessQueue(qName, openOptions, null, null,
null); // set the put message options
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;
gmo.options = gmo.options + MQC.MQGMO_WAIT;
gmo.options = gmo.options + MQC.MQGMO_FAIL_IF_QUIESCING;
gmo.waitInterval = 3000; // build mssage
MQMessage inMsg = new MQMessage(); // get the message from Q
queue.get(inMsg, gmo); // read the data from the message
String msgString = inMsg.readUTF();
System.out.println("The Message from Q is :" + msgString); // check if message is of type request message and reply to the
// request
if (inMsg.messageFlags == MQC.MQMT_REQUEST) {
System.out.println("Praparing to reply to the request" ;
String replyQueueName = inMsg.replyToQueueName;
int openOptions2 = MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING;
MQQueue respQueue = qMgr
.accessQueue(replyQueueName, openOptions2,
inMsg.replyToQueueManagerName, null, null);
MQMessage respMessage = new MQMessage();
respMessage.correlationId = inMsg.messageId;
MQPutMessageOptions pmo = new MQPutMessageOptions();
respMessage.messageFlags = MQC.MQMT_REPLY;
String response = "reply from responder program";
respMessage.writeUTF(response);
respQueue.put(respMessage, pmo);
System.out.println("The response sucessfully send!" ;
qMgr.commit();
respQueue.close();
}
queue.close();
qMgr.disconnect();
} catch (MQException ex) {
System.out.println("Completion code is:" + ex.completionCode
+ " reason code is:" + ex.reasonCode);
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
转载该文章请注明转载地址。
该文章转至冠尔培训官方技术博客:http://www.javalord.com/javalordblog
冠尔培训网站地址:http://www.javalord.com
No Comments
Posted in JavaEE技术, Java应用开发
有关MQ中的一些基本概念(二)
25
Aug
MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。
9. 通道:
定义:一个通道为逻辑上的一个通信连接。在MQSeries中有两个不同类型的通道。
Ø 消息通道(Message Channels):通过消息通道代理(Message Channel Agents,MCAs)。消息通道连接两个队列管理器。这样的通道是单向的,它包括三个MCAs,一个发送方的,一个接受方的核通信协议。MCA是一个程序,这个程序将消息通过一个通信连接从传输队列传递到目标队列。为了进行双向通信,你必须定义包含发送方和接受方的一对通道。而MCA就作为搬运者的角色。
消息通道的分类:
发送通道(Sender)
接受通道(Receiver)
服务器通道(Server)
请求器通道(Requester)
集群佳节又重阳发送通道(Cluster Sender)
集群接受通道(Cluster Receiver)
Ø MQI通道:消息队列接口(Message Queue Interface,MQI)通道用来连接MQSeries客户端和在服务器上的队列管理器。客户端在没有自己的队列管理器是可以使用这种方式。MQI通道是双向的。
下图显示了两种通道的作用:
上图表示了这两种通道的类型。你可以看到四台机器,两个客户端通过MQI通道连接到服务器机器,另外,通过两个单向的通道,这个服务器与另一个服务器连接。有些通道可以由MQSeries自动定义。使用什么类型的通道,取决于在队列管理器间的session是如何初始化的,以及使用它们的目标是什么。
为了适应传递非持久性的消息,消息通道可以运行在两种速率上,快速和一般。快速通道提高了性能,但是,非持久性消息在通道差的时候可能丢失。
一个通道可以使用下面的传输类型:SNA、LU6.2、TCP/IP、NetBIOS、SPX和DECNet。不是每个平台都支持上面的所有类型。
10. 消息队列:
队列被定义为属于队列管理器的对象。MQSeries能识别一些不同类型的队列,这些队列都有着特殊的用途,这些队肯能在你的本地机器上,也可能属于你所连接到的队列管理器,或者在你的服务器上(如果你是客户端)。下面详细说明不同类型的队列和他们的用途。
Ø 本地队列(Local Queue):如果一个队列属于应用程序所连接的队列管理器,这个队列是本地队列。本地队列可以用来为同一个队列管理器的程序存储信息。例如,程序A和程序B都有自己的用于发送和接收信息的队列。由于队列管理器服务于这些程序,所以这些队列都是本地的。
注意:两个程序不必再同一个工作站运行。客户端通常使用服务器上的队列管理器。
Ø 集群队列(Cluster Queue):集群队列是本地队列,它暴露于在这个集群中的所有队列管理器,也就是说,属于该集群的队列管理器可以不必远程定义通道就能发送到集群队列。
Ø 远程队列(Remote Queue):如果队列被另外的队列管理器所有,这个队列则为远程队列。远程队列定义指的是对远程队列的本地定义。远程队列不是一个实实在在的的队列,它是包含属于不同队列管理器的队列特性的结构体。程序开发者可以像使用本地队列一样,通过名字来使用远程队列。MQSeries管理器定义了队列的实际位置。一个远程队列与传输队列相关联。
注意:应用程序不可以从远程队列读取消息。对于集群队列而言,你不必进行远程队列定义。
Ø 传输队列(Transmisssion Queue):传输队列是有着特殊用途的本地队列。一个远程队列与一个传输队列相关联。当消息发送到属于另一个队列管理器的队列时,传输队列作为一个中间环节使用。
典型地,对每一个远程队列管理器(或机器)建立一个传输队列。所有要写入远程队列管理器的消息实际上都发送到传输队列中。消息将会从传输队列中读出然后发送到队列管理器。
Ø 动态队列(Dynamic Queue):动态队列在应用程序需要的时候被定义为"on the fly"。动态队列由队列管理器维持并且在程序退出时会被自动删除。动态队列是本地队列。动态队列用在对话型应用程序,用于存储中间结果。动态队列可以是:
ü 临时队列,在队列管理器重起后不能恢复。
ü 持久型队列,在队列管理器重启后可以恢复。
Ø 别名队列(Alias Queue):别名队列不是真实的队列而仅是一个定义。他们用来给相同的物理队列起不同的名字。这使得多个程序可以使用不同的名字,不同的属性来访问队列。
Ø 模板队列(Model Queue):模板队列不是一个真实的队列,它只是当动态队列创建的时候的一个属性集合。
Ø 启动队列(Initiation Queue):启动队列是一个本地队列。当某种条件在一个本地队列产生时,队列管理器会将触发消息放到启动队列中。例如,当消息放到一个空的队列或传输队列。这种触发消息对程序的启动是透明的。两个MQSeries程序监控启动队列和读取触发消息。触发起监控哪一个程序开始运行,即以哪个通道初始化器开始了两个队列管理器之间的消息发送。
注意:程序本身并不用关注启动队列,而在启动队列中所使用的触发机制是设计和编程的有力工具。
Ø 回复队列(Reply-to-Queue):一个请求消息必须给出响应程序将应答消息发送到的队列名字。也可以把这理解为给出返回地址,要将这个队列的名字以及拥有该队列的队列管理器的名字一起存储到消息的头部。
Ø 死信队列(Dead-Letter Queue):当一个队列管理器不能传输消息时,它必须能够处理这种情况。下面给出一些情景:
ü 目标队列已经满了
ü 目标队列不能退出
ü 目标队列被禁止接受消息
ü 发送方没被授权使用目标队列
ü 消息太长
ü 消息包含消息顺序号的副本
当以上情形中的一种或几种发生,那么消息就会被发送到死信队列中。死信队列是在队列管理器产生的时候就被定义好的。当消息不能传递时,死信队列作为这些消息的仓库。
转载该文章请注明转载地址。
该文章转至冠尔培训官方技术博客:http://www.javalord.com/javalordblog
冠尔培训网站地址:http://www.javalord.com
No Comments
Posted in JavaEE技术, Java应用开发
有关MQ中的一些基本概念(一)
25
Aug
最近在项目中用了MQ中间件产品,看了很多东西,回过头来觉得应该把MQ的基本概念加深一下认识,所以把一些基本的概念总结了出来。
有关消息的概念:
1. 消息包括两部分:
Ø 从一个程序传递到另一个程序的数据部分
Ø 消息描述符或叫消息头
消息描述符定义了MessageID、控制信息(包括如:消息的类型、终结时间、correlationId、优先级、以及等待应答的队列名字)。
消息的长度可以从4M~100M,取决于使用的MQSeries的版本。
2. 消息片断和分组:
在MQSeries5中支持消息的分片和分组。如果允许,队列管理器把不适合队列的消息自动分片。在接受端,程序可以通过接受分片来获得整个消息。
第二种分片是应用程序根据物理边界和缓存大小来对消息进行分片。程序把每一个片断作为独立的物理消息,多个物理消息构成一个逻辑消息。队列管理器会保证分片的次序。
为了减少网络传输的时间,也可以把多个小分片组成一个大的物理消息。消息被发送到目的地,并在那里被拆解回小的分片。
3. 发送列表:
使用MQSeries5,你可以利用一次MQPUT调用来把消息发送到一个或多个目的队列。这是通过动态发送列表来完成的。发送列表可以是一个文件,在程序启动的时候读取该文件。该文件可以在任何时候被修改。该文件包含一个列表,该列表保存队列的名字和拥有该队列的队列管理器的名字的对应关系。消息发送到具有相同队列管理器的多个队列的时候,该消息在网上实际只被传输一次,以便减少网络传输。接受的队列管理器复制该消息,并把它们发送到其他的目的队列上。这个功能称作:Late fan-out。
4. 消息的类型:
Ø 请求消息(Request Message):请求消息需要应答。从客户端发往服务器的查询和更新消息往往是一条请求消息。请求消息中应该包含回复消息的路由信息。即回复消息发往什么地方。
Ø 回复消息(Reply Message):回复消息是对请求消息的回应。请求消息中的信息决定论了回应消息的目的地。处理请求和回应的应用程序控制着消息间的关联,这种关联和队列管理器没有关系。消息自身带有足够的信息供应用程序实现这种关联。
Ø 报文消息(Datagram Message):数据报消息是不需要回复的消息,报文消息只是一次单向的信息传递。
Ø 报告消息(Report Message):报告消息用于对一些系统故障的响应。有些报告消息是由应用程序创建的,有些报告消息是由队列管理器创建的。后一种情况是由于远程队列以满或者远程队列不存在引起消息不能正确发送。最初发送消息的应用程序不能检测到这种错误,只有等待远程队列管理器创建了这样一条报告消息并发往本地队列管理器之后,应用程序才能做相应的处理。
5. 持久性消息和非持久性消息:
如果一个消息在任何情况下都必须发送到目的地就是持久性消息。如果一个消息不能准时到达目的地时可以被丢弃,就是非持久性消息。传递持久性消息的时候要记录log,以便系统故障时恢复,非持久性消息在重起后不能被恢复。
6. 消息描述符:
下表包含一些消息描述符的属性字段,它们能够说明一些由队列管理器提供的功能。
7. 队列管理器:
MQSeries的核心是队列管理器(简称,MQM)。队列管理器的工作是管理程序中要使用的队列和消息。它为程序提供用来通信的MQI(Message Queuing Interface)。程序通过API调用来获得队列管理器提供的功能。例如,MQPUT API调用来把消息放到队列中,而另一个程序通过MQGET API调用来从中读取消息。如下图所示。
程序不但可以把消息通过队列管理器发送到运行在同一个机器上的另一个应用程序,也可以发送到运行在远程系统上的应用程序。远程系统拥有自己的队列管理器和队列。如下图所示。
利用现有的网络协议,如TCP/IP、SNA或SPX,一个队列管理器通过通道把消息发送到另一个队列管理器。同一机器上的多个队列管理器之间的通信也需要使用通道。
8. 队列管理器的集群:
MQSeries for MVS/ESA以及针对分布式平台的5.1版提供可以把多个队列管理器集群的功能。形成集群的队列管理器可以运行在相同的机器上,也可以运行在不同平台下的不同机器上。通常,利用维护一个仓库,这个仓库包含所有队列管理器以及队列的信息。维护仓库的工作由集群的队列管理器中的一个来完成。另外还需要一个队列管理器来维护一个局部的仓库,这个仓库用来维护一些对象。仓库允许集群内的任意队列管理器来查找有关的集群队列以及谁拥有该队列。队列管理器使用特殊的集群通道来交换信息。
集群允许一个队列有多个实例(具有相同名字的队列),这些队列分布在不同的队列管理器中。考虑到均衡工作量,队列管理器可以把消息发送到一个应用程序的多个实例中。
在通常的分布式处理过程中,我们把消息发送到指定的队列管理器拥有的指定的队列中。所有的消息由队列管理器发送到处在发送端的发送队列中。这个传输队列具有与目的队列管理器相同的名字。消息通道代理通过网络传递消息,并把消息放到目的队列中。下图说明了传输队列(Xmit Queue)与目的队列管理器的关系。
在集群中,你可以把消息发送到集群中的指定名字的队列,在下图中用云朵表示。你需要指定目标队列的名字,而不需要指定远程队列定义的名字。集群不必有远程队列定义。对于当你把消息发送到不是集群成员的队列管理器中,这是很有用的。你也可以指定队列管理器并直接把消息发送到明确的队列,但是通常这些都留给队列管理器来决定,队列在什么地方以及发送到哪个队列。
MQSeries集群表现为一个队列的多个实例共存的地方。消息的流动按照管理员的要求,以便使用变化的可用性和吞吐量的需求。以上的过程都是动态完成的,不必对管理器进行复杂的配置和控制。另外,开发人员不必考虑过多的队列,对他们来说就像是在向一个单独的队列些消息。
MQ发布消息时通过循环发送来完成的。你也可以改变这种默认的行为。下图,消息要发送到3个位于集群中名字为A的队列。这三个队列管理器都包含一个名字为A的队列。默认情况下,消息的发送顺序为:先是队列管理器1中的QA,然后是队列管理器2中的QA,再是队列管理器3中QA,再循环到队列管理器1中的QA。
另一种情况,如果第三个队列管理
器down掉,并且第三个QB的实例不可用。由于消息会被发送到所有的队列管理器和想要达到的队列,所以发送方的队列管理器会意识到问题。当消息只能被发送到前两个实例时,发送的队列管理器就知道第三个队列的实例有问题。这时,有关集群有问题的特殊消息会被发送到所有的队列管理器。
转载该文章请注明转载地址。
该文章转至冠尔培训官方技术博客:http://www.javalord.com/javalordblog
冠尔培训网站地址:http://www.javalord.com
No Comments
Posted in JavaEE技术, Java应用开发
使用MQ时遇到的CCSID问题
17
May
今天在做MQ的一个例子程序的时候,遇到了CCSID问题,还好弄了半天终于搞定了 :)
今天把MQ文档中的例子拿来做了一下,在调试的过程中遇到了CCSID问题。
下面先把程序总体思路说一下,软硬件的环境是:我使用一台PC机做服务器,并在这台机器上安装WebSphere MQ的服务器。然后把另外一台PC机作为客户端,并且在这台机器上安装WebSphere MQ的Windows 版客户端。服务器端的操作系统是Windows 2000 Professional,客户端是Windows XP。
我做的这个例子是通过客户端的方式来向服务器传递消息。服务器上的MQ中的参数为:
hostname:neu
MQM name: QM_guo
Channel name:ch_server
Q name:Q_guo
PtpSender.java(用于向服务器端传递消息) PtpReceiver.java(用于从服务器端得到消息)
/*
* Created on 2005-1-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*//**
* @author Ralph
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
import com.ibm.mq.*;
public class PtpSender { public static void main(String[] args) {
try{
String hostName="neu";
String channel="ch_server";
String qManager="QM_guo";
String qName="Q_guo"; //set up the MQEnvironment properties for the client
MQEnvironment.hostname=hostName;
MQEnvironment.channel=channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID=1381;
//connetion to Q Manager
MQQueueManager qMgr=new MQQueueManager(qManager); //set up the open options
int openOptions=MQC.MQOO_OUTPUT|MQC.MQOO_FAIL_IF_QUIESCING; //open the Q
MQQueue queue=qMgr.accessQueue(qName,openOptions,null,null,null); //set the put message options,will use the default settings
MQPutMessageOptions pmo=new MQPutMessageOptions(); //build a message and write data
MQMessage outMsg=new MQMessage(); //prepare message with the user data
String msgString="Test Message from PtpSender program";
outMsg.writeUTF(msgString); //Now we put the message on the Q
queue.put(outMsg,pmo); //commit the transaction
qMgr.commit(); System.out.println("The message has been sussesfully put #####"); //close the Q and QManager objects
queue.close();
qMgr.disconnect(); }
catch(MQException ex){
System.out.println("completion code:"+ex.completionCode+" Reason code:"+ex.reasonCode);
ex.printStackTrace();
}
catch(Exception e){
e.printStackTrace();
}
}
}import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.*;
import com.ibm.mq.*;/*
* Created on 2005-1-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*//**
* @author Ralph
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class PtpReceiver { public static void main(String[] args) {
try {
String hostName = "neu";
String channel = "ch_server";
String qManager = "QM_guo";
String qName = "Q_guo"; // set up the MQEnvironment properties for the client
MQEnvironment.hostname = hostName;
MQEnvironment.channel = channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID = 1381;
// connetion to Q Manager
MQQueueManager qMgr = new MQQueueManager(qManager); // set up the open options
int openOptions = MQC.MQOO_INPUT_SHARED
| MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue queue = qMgr.accessQueue(qName, openOptions, null, null,
null); // set get message options
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;
gmo.options = gmo.options + MQC.MQGMO_WAIT;
gmo.options = gmo.options + MQC.MQGMO_FAIL_IF_QUIESCING;
gmo.waitInterval = 3000; // build mssage
MQMessage inMsg = new MQMessage(); // get the message from Q
queue.get(inMsg, gmo); // read the data from the message
String msgString = inMsg.readUTF();
System.out.println("The Message from Q is :" + msgString); // commit the trasaction
qMgr.commit(); // close the Q and connection
queue.close();
qMgr.disconnect();
} catch (MQException ex) {
System.out.println("Completion code is:" + ex.completionCode
+ " reason code is:" + ex.reasonCode);
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
文档中的源程序没有红色部分的代码,这是我在调试的过程中出现提示:队列管理器没有打开CCSID功能的提示。经过在网上找资料和查看文档,最后知道是由于Windows 2000与Windows XP CCSID不同导致的,所以在代码中对CCSID的值进行显示的负值后问题解决了。
CCSID的值在AIX上一般设为1383,如果要支持GBK则设为1386,在WIN上设为1381。
一个程序把消息放在Q上,请求的程序会在把消息放到Q上之前设置replyToQueue和replyToQueueManager消息标题属性。而后它打开回复队列并等待correlationId匹配已发出请求消息的MessageId值的消息。
另一个程序是在接受到消息后判断如果是请求消息的话,则生成回复消息并发到请求消息指定的消息队列上。它还将拷贝请求消息的MessageId到回复消息的correlationId消息标题字段上。
程序的代码如下:
import com.ibm.mq.*;/**
* @author ralph
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class Requester { public static void main(String[] args) {
try {
String hostName = "neu";
String channel = "ch_server";
String qManager = "QM_guo";
String requestQueue = "Q_request";
String replyToQueue = "Q_reply";
String replyToQueueManager = "QM_guo"; // set up the MQEnvironment properties for the client
MQEnvironment.hostname = hostName;
MQEnvironment.channel = channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID = 1381; // connetion to Q Manager
MQQueueManager qMgr = new MQQueueManager(qManager); // set up the open options
int openOptions = MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue queue = qMgr.accessQueue(requestQueue, openOptions, null,
null, null); // set the put message options,will use the default settings
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options = pmo.options + MQC.MQPMO_NEW_MSG_ID;
pmo.options = pmo.options + MQC.MQPMO_SYNCPOINT; // build a message and write data
MQMessage outMsg = new MQMessage();
outMsg.messageFlags = MQC.MQMT_REQUEST;
outMsg.replyToQueueManagerName = replyToQueueManager;
outMsg.replyToQueueName = replyToQueue; // prepare message with the user data
String msgString = "The request message from requester program!";
outMsg.writeUTF(msgString); // Now we put the message on the Q
queue.put(outMsg, pmo); // commit the transaction
qMgr.commit(); System.out.println("The message has been sussesfully put #####" ; // close the Q
queue.close(); // set up the open options
int openOptions2 = MQC.MQOO_INPUT_SHARED
| MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue respQueue = qMgr.accessQueue(replyToQueue, openOptions2,
null, null, null);
MQMessage respMessage = new MQMessage();
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;
gmo.options = gmo.options + MQC.MQGMO_WAIT;
gmo.matchOptions = MQC.MQMO_MATCH_CORREL_ID;
gmo.waitInterval = 10000;
respMessage.correlationId = outMsg.correlationId; // get the response message
respQueue.get(respMessage, gmo);
String response = respMessage.readUTF();
System.out.println("The response message is:" + response); qMgr.commit();
respQueue.close();
qMgr.disconnect();
} catch (MQException ex) {
System.out.println("Completion code is:" + ex.completionCode
+ " reason code is:" + ex.reasonCode);
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}/**
* @author ralph
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/import com.ibm.mq.*;public class Responder { public static void main(String[] args) {
try {
String hostName = "neu";
String channel = "ch_server";
String qManager = "QM_guo";
String qName = "Q_request"; // set up the MQEnvironment properties for the client
MQEnvironment.hostname = hostName;
MQEnvironment.channel = channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID = 1381; // connetion to Q Manager
MQQueueManager qMgr = new MQQueueManager(qManager); // set up the open options
int openOptions = MQC.MQOO_INPUT_SHARED
| MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue queue = qMgr.accessQueue(qName, openOptions, null, null,
null); // set the put message options
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;
gmo.options = gmo.options + MQC.MQGMO_WAIT;
gmo.options = gmo.options + MQC.MQGMO_FAIL_IF_QUIESCING;
gmo.waitInterval = 3000; // build mssage
MQMessage inMsg = new MQMessage(); // get the message from Q
queue.get(inMsg, gmo); // read the data from the message
String msgString = inMsg.readUTF();
System.out.println("The Message from Q is :" + msgString); // check if message is of type request message and reply to the
// request
if (inMsg.messageFlags == MQC.MQMT_REQUEST) {
System.out.println("Praparing to reply to the request" ;
String replyQueueName = inMsg.replyToQueueName;
int openOptions2 = MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING;
MQQueue respQueue = qMgr
.accessQueue(replyQueueName, openOptions2,
inMsg.replyToQueueManagerName, null, null);
MQMessage respMessage = new MQMessage();
respMessage.correlationId = inMsg.messageId;
MQPutMessageOptions pmo = new MQPutMessageOptions();
respMessage.messageFlags = MQC.MQMT_REPLY;
String response = "reply from responder program";
respMessage.writeUTF(response);
respQueue.put(respMessage, pmo);
System.out.println("The response sucessfully send!" ;
qMgr.commit();
respQueue.close();
}
queue.close();
qMgr.disconnect();
} catch (MQException ex) {
System.out.println("Completion code is:" + ex.completionCode
+ " reason code is:" + ex.reasonCode);
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
转载该文章请注明转载地址。
该文章转至冠尔培训官方技术博客:http://www.javalord.com/javalordblog
冠尔培训网站地址:http://www.javalord.com
No Comments
Posted in JavaEE技术, Java应用开发
有关MQ中的一些基本概念(二)
25
Aug
MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。MQ的基本概念续。
9. 通道:
定义:一个通道为逻辑上的一个通信连接。在MQSeries中有两个不同类型的通道。
Ø 消息通道(Message Channels):通过消息通道代理(Message Channel Agents,MCAs)。消息通道连接两个队列管理器。这样的通道是单向的,它包括三个MCAs,一个发送方的,一个接受方的核通信协议。MCA是一个程序,这个程序将消息通过一个通信连接从传输队列传递到目标队列。为了进行双向通信,你必须定义包含发送方和接受方的一对通道。而MCA就作为搬运者的角色。
消息通道的分类:
发送通道(Sender)
接受通道(Receiver)
服务器通道(Server)
请求器通道(Requester)
集群佳节又重阳发送通道(Cluster Sender)
集群接受通道(Cluster Receiver)
Ø MQI通道:消息队列接口(Message Queue Interface,MQI)通道用来连接MQSeries客户端和在服务器上的队列管理器。客户端在没有自己的队列管理器是可以使用这种方式。MQI通道是双向的。
下图显示了两种通道的作用:
上图表示了这两种通道的类型。你可以看到四台机器,两个客户端通过MQI通道连接到服务器机器,另外,通过两个单向的通道,这个服务器与另一个服务器连接。有些通道可以由MQSeries自动定义。使用什么类型的通道,取决于在队列管理器间的session是如何初始化的,以及使用它们的目标是什么。
为了适应传递非持久性的消息,消息通道可以运行在两种速率上,快速和一般。快速通道提高了性能,但是,非持久性消息在通道差的时候可能丢失。
一个通道可以使用下面的传输类型:SNA、LU6.2、TCP/IP、NetBIOS、SPX和DECNet。不是每个平台都支持上面的所有类型。
10. 消息队列:
队列被定义为属于队列管理器的对象。MQSeries能识别一些不同类型的队列,这些队列都有着特殊的用途,这些队肯能在你的本地机器上,也可能属于你所连接到的队列管理器,或者在你的服务器上(如果你是客户端)。下面详细说明不同类型的队列和他们的用途。
Ø 本地队列(Local Queue):如果一个队列属于应用程序所连接的队列管理器,这个队列是本地队列。本地队列可以用来为同一个队列管理器的程序存储信息。例如,程序A和程序B都有自己的用于发送和接收信息的队列。由于队列管理器服务于这些程序,所以这些队列都是本地的。
注意:两个程序不必再同一个工作站运行。客户端通常使用服务器上的队列管理器。
Ø 集群队列(Cluster Queue):集群队列是本地队列,它暴露于在这个集群中的所有队列管理器,也就是说,属于该集群的队列管理器可以不必远程定义通道就能发送到集群队列。
Ø 远程队列(Remote Queue):如果队列被另外的队列管理器所有,这个队列则为远程队列。远程队列定义指的是对远程队列的本地定义。远程队列不是一个实实在在的的队列,它是包含属于不同队列管理器的队列特性的结构体。程序开发者可以像使用本地队列一样,通过名字来使用远程队列。MQSeries管理器定义了队列的实际位置。一个远程队列与传输队列相关联。
注意:应用程序不可以从远程队列读取消息。对于集群队列而言,你不必进行远程队列定义。
Ø 传输队列(Transmisssion Queue):传输队列是有着特殊用途的本地队列。一个远程队列与一个传输队列相关联。当消息发送到属于另一个队列管理器的队列时,传输队列作为一个中间环节使用。
典型地,对每一个远程队列管理器(或机器)建立一个传输队列。所有要写入远程队列管理器的消息实际上都发送到传输队列中。消息将会从传输队列中读出然后发送到队列管理器。
Ø 动态队列(Dynamic Queue):动态队列在应用程序需要的时候被定义为"on the fly"。动态队列由队列管理器维持并且在程序退出时会被自动删除。动态队列是本地队列。动态队列用在对话型应用程序,用于存储中间结果。动态队列可以是:
ü 临时队列,在队列管理器重起后不能恢复。
ü 持久型队列,在队列管理器重启后可以恢复。
Ø 别名队列(Alias Queue):别名队列不是真实的队列而仅是一个定义。他们用来给相同的物理队列起不同的名字。这使得多个程序可以使用不同的名字,不同的属性来访问队列。
Ø 模板队列(Model Queue):模板队列不是一个真实的队列,它只是当动态队列创建的时候的一个属性集合。
Ø 启动队列(Initiation Queue):启动队列是一个本地队列。当某种条件在一个本地队列产生时,队列管理器会将触发消息放到启动队列中。例如,当消息放到一个空的队列或传输队列。这种触发消息对程序的启动是透明的。两个MQSeries程序监控启动队列和读取触发消息。触发起监控哪一个程序开始运行,即以哪个通道初始化器开始了两个队列管理器之间的消息发送。
注意:程序本身并不用关注启动队列,而在启动队列中所使用的触发机制是设计和编程的有力工具。
Ø 回复队列(Reply-to-Queue):一个请求消息必须给出响应程序将应答消息发送到的队列名字。也可以把这理解为给出返回地址,要将这个队列的名字以及拥有该队列的队列管理器的名字一起存储到消息的头部。
Ø 死信队列(Dead-Letter Queue):当一个队列管理器不能传输消息时,它必须能够处理这种情况。下面给出一些情景:
ü 目标队列已经满了
ü 目标队列不能退出
ü 目标队列被禁止接受消息
ü 发送方没被授权使用目标队列
ü 消息太长
ü 消息包含消息顺序号的副本
当以上情形中的一种或几种发生,那么消息就会被发送到死信队列中。死信队列是在队列管理器产生的时候就被定义好的。当消息不能传递时,死信队列作为这些消息的仓库。
转载该文章请注明转载地址。
该文章转至冠尔培训官方技术博客:http://www.javalord.com/javalordblog
冠尔培训网站地址:http://www.javalord.com
No Comments
Posted in JavaEE技术, Java应用开发
有关MQ中的一些基本概念(一)
25
Aug
最近在项目中用了MQ中间件产品,看了很多东西,回过头来觉得应该把MQ的基本概念加深一下认识,所以把一些基本的概念总结了出来。
有关消息的概念:
1. 消息包括两部分:
Ø 从一个程序传递到另一个程序的数据部分
Ø 消息描述符或叫消息头
消息描述符定义了MessageID、控制信息(包括如:消息的类型、终结时间、correlationId、优先级、以及等待应答的队列名字)。
消息的长度可以从4M~100M,取决于使用的MQSeries的版本。
2. 消息片断和分组:
在MQSeries5中支持消息的分片和分组。如果允许,队列管理器把不适合队列的消息自动分片。在接受端,程序可以通过接受分片来获得整个消息。
第二种分片是应用程序根据物理边界和缓存大小来对消息进行分片。程序把每一个片断作为独立的物理消息,多个物理消息构成一个逻辑消息。队列管理器会保证分片的次序。
为了减少网络传输的时间,也可以把多个小分片组成一个大的物理消息。消息被发送到目的地,并在那里被拆解回小的分片。
3. 发送列表:
使用MQSeries5,你可以利用一次MQPUT调用来把消息发送到一个或多个目的队列。这是通过动态发送列表来完成的。发送列表可以是一个文件,在程序启动的时候读取该文件。该文件可以在任何时候被修改。该文件包含一个列表,该列表保存队列的名字和拥有该队列的队列管理器的名字的对应关系。消息发送到具有相同队列管理器的多个队列的时候,该消息在网上实际只被传输一次,以便减少网络传输。接受的队列管理器复制该消息,并把它们发送到其他的目的队列上。这个功能称作:Late fan-out。
4. 消息的类型:
Ø 请求消息(Request Message):请求消息需要应答。从客户端发往服务器的查询和更新消息往往是一条请求消息。请求消息中应该包含回复消息的路由信息。即回复消息发往什么地方。
Ø 回复消息(Reply Message):回复消息是对请求消息的回应。请求消息中的信息决定论了回应消息的目的地。处理请求和回应的应用程序控制着消息间的关联,这种关联和队列管理器没有关系。消息自身带有足够的信息供应用程序实现这种关联。
Ø 报文消息(Datagram Message):数据报消息是不需要回复的消息,报文消息只是一次单向的信息传递。
Ø 报告消息(Report Message):报告消息用于对一些系统故障的响应。有些报告消息是由应用程序创建的,有些报告消息是由队列管理器创建的。后一种情况是由于远程队列以满或者远程队列不存在引起消息不能正确发送。最初发送消息的应用程序不能检测到这种错误,只有等待远程队列管理器创建了这样一条报告消息并发往本地队列管理器之后,应用程序才能做相应的处理。
5. 持久性消息和非持久性消息:
如果一个消息在任何情况下都必须发送到目的地就是持久性消息。如果一个消息不能准时到达目的地时可以被丢弃,就是非持久性消息。传递持久性消息的时候要记录log,以便系统故障时恢复,非持久性消息在重起后不能被恢复。
6. 消息描述符:
下表包含一些消息描述符的属性字段,它们能够说明一些由队列管理器提供的功能。
7. 队列管理器:
MQSeries的核心是队列管理器(简称,MQM)。队列管理器的工作是管理程序中要使用的队列和消息。它为程序提供用来通信的MQI(Message Queuing Interface)。程序通过API调用来获得队列管理器提供的功能。例如,MQPUT API调用来把消息放到队列中,而另一个程序通过MQGET API调用来从中读取消息。如下图所示。
程序不但可以把消息通过队列管理器发送到运行在同一个机器上的另一个应用程序,也可以发送到运行在远程系统上的应用程序。远程系统拥有自己的队列管理器和队列。如下图所示。
利用现有的网络协议,如TCP/IP、SNA或SPX,一个队列管理器通过通道把消息发送到另一个队列管理器。同一机器上的多个队列管理器之间的通信也需要使用通道。
8. 队列管理器的集群:
MQSeries for MVS/ESA以及针对分布式平台的5.1版提供可以把多个队列管理器集群的功能。形成集群的队列管理器可以运行在相同的机器上,也可以运行在不同平台下的不同机器上。通常,利用维护一个仓库,这个仓库包含所有队列管理器以及队列的信息。维护仓库的工作由集群的队列管理器中的一个来完成。另外还需要一个队列管理器来维护一个局部的仓库,这个仓库用来维护一些对象。仓库允许集群内的任意队列管理器来查找有关的集群队列以及谁拥有该队列。队列管理器使用特殊的集群通道来交换信息。
集群允许一个队列有多个实例(具有相同名字的队列),这些队列分布在不同的队列管理器中。考虑到均衡工作量,队列管理器可以把消息发送到一个应用程序的多个实例中。
在通常的分布式处理过程中,我们把消息发送到指定的队列管理器拥有的指定的队列中。所有的消息由队列管理器发送到处在发送端的发送队列中。这个传输队列具有与目的队列管理器相同的名字。消息通道代理通过网络传递消息,并把消息放到目的队列中。下图说明了传输队列(Xmit Queue)与目的队列管理器的关系。
在集群中,你可以把消息发送到集群中的指定名字的队列,在下图中用云朵表示。你需要指定目标队列的名字,而不需要指定远程队列定义的名字。集群不必有远程队列定义。对于当你把消息发送到不是集群成员的队列管理器中,这是很有用的。你也可以指定队列管理器并直接把消息发送到明确的队列,但是通常这些都留给队列管理器来决定,队列在什么地方以及发送到哪个队列。
MQSeries集群表现为一个队列的多个实例共存的地方。消息的流动按照管理员的要求,以便使用变化的可用性和吞吐量的需求。以上的过程都是动态完成的,不必对管理器进行复杂的配置和控制。另外,开发人员不必考虑过多的队列,对他们来说就像是在向一个单独的队列些消息。
MQ发布消息时通过循环发送来完成的。你也可以改变这种默认的行为。下图,消息要发送到3个位于集群中名字为A的队列。这三个队列管理器都包含一个名字为A的队列。默认情况下,消息的发送顺序为:先是队列管理器1中的QA,然后是队列管理器2中的QA,再是队列管理器3中QA,再循环到队列管理器1中的QA。
另一种情况,如果第三个队列管理
器down掉,并且第三个QB的实例不可用。由于消息会被发送到所有的队列管理器和想要达到的队列,所以发送方的队列管理器会意识到问题。当消息只能被发送到前两个实例时,发送的队列管理器就知道第三个队列的实例有问题。这时,有关集群有问题的特殊消息会被发送到所有的队列管理器。
转载该文章请注明转载地址。
该文章转至冠尔培训官方技术博客:http://www.javalord.com/javalordblog
冠尔培训网站地址:http://www.javalord.com
No Comments
Posted in JavaEE技术, Java应用开发
使用MQ时遇到的CCSID问题
17
May
今天在做MQ的一个例子程序的时候,遇到了CCSID问题,还好弄了半天终于搞定了 :)
今天把MQ文档中的例子拿来做了一下,在调试的过程中遇到了CCSID问题。
下面先把程序总体思路说一下,软硬件的环境是:我使用一台PC机做服务器,并在这台机器上安装WebSphere MQ的服务器。然后把另外一台PC机作为客户端,并且在这台机器上安装WebSphere MQ的Windows 版客户端。服务器端的操作系统是Windows 2000 Professional,客户端是Windows XP。
我做的这个例子是通过客户端的方式来向服务器传递消息。服务器上的MQ中的参数为:
hostname:neu
MQM name: QM_guo
Channel name:ch_server
Q name:Q_guo
PtpSender.java(用于向服务器端传递消息) PtpReceiver.java(用于从服务器端得到消息)
/*
* Created on 2005-1-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*//**
* @author Ralph
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
import com.ibm.mq.*;
public class PtpSender { public static void main(String[] args) {
try{
String hostName="neu";
String channel="ch_server";
String qManager="QM_guo";
String qName="Q_guo"; //set up the MQEnvironment properties for the client
MQEnvironment.hostname=hostName;
MQEnvironment.channel=channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID=1381;
//connetion to Q Manager
MQQueueManager qMgr=new MQQueueManager(qManager); //set up the open options
int openOptions=MQC.MQOO_OUTPUT|MQC.MQOO_FAIL_IF_QUIESCING; //open the Q
MQQueue queue=qMgr.accessQueue(qName,openOptions,null,null,null); //set the put message options,will use the default settings
MQPutMessageOptions pmo=new MQPutMessageOptions(); //build a message and write data
MQMessage outMsg=new MQMessage(); //prepare message with the user data
String msgString="Test Message from PtpSender program";
outMsg.writeUTF(msgString); //Now we put the message on the Q
queue.put(outMsg,pmo); //commit the transaction
qMgr.commit(); System.out.println("The message has been sussesfully put #####"); //close the Q and QManager objects
queue.close();
qMgr.disconnect(); }
catch(MQException ex){
System.out.println("completion code:"+ex.completionCode+" Reason code:"+ex.reasonCode);
ex.printStackTrace();
}
catch(Exception e){
e.printStackTrace();
}
}
}import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.*;
import com.ibm.mq.*;/*
* Created on 2005-1-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*//**
* @author Ralph
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class PtpReceiver { public static void main(String[] args) {
try {
String hostName = "neu";
String channel = "ch_server";
String qManager = "QM_guo";
String qName = "Q_guo"; // set up the MQEnvironment properties for the client
MQEnvironment.hostname = hostName;
MQEnvironment.channel = channel;
MQEnvironment.properties.put(MQC.TRANSPORT_PROPERTY,
MQC.TRANSPORT_MQSERIES);
MQEnvironment.CCSID = 1381;
// connetion to Q Manager
MQQueueManager qMgr = new MQQueueManager(qManager); // set up the open options
int openOptions = MQC.MQOO_INPUT_SHARED
| MQC.MQOO_FAIL_IF_QUIESCING; // open the Q
MQQueue queue = qMgr.accessQueue(qName, openOptions, null, null,
null); // set get message options
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;
gmo.options = gmo.options + MQC.MQGMO_WAIT;
gmo.options = gmo.options + MQC.MQGMO_FAIL_IF_QUIESCING;
gmo.waitInterval = 3000; // build mssage
MQMessage inMsg = new MQMessage(); // get the message from Q
queue.get(inMsg, gmo); // read the data from the message
String msgString = inMsg.readUTF();
System.out.println("The Message from Q is :" + msgString); // commit the trasaction
qMgr.commit(); // close the Q and connection
queue.close();
qMgr.disconnect();
} catch (MQException ex) {
System.out.println("Completion code is:" + ex.completionCode
+ " reason code is:" + ex.reasonCode);
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
文档中的源程序没有红色部分的代码,这是我在调试的过程中出现提示:队列管理器没有打开CCSID功能的提示。经过在网上找资料和查看文档,最后知道是由于Windows 2000与Windows XP CCSID不同导致的,所以在代码中对CCSID的值进行显示的负值后问题解决了。
CCSID的值在AIX上一般设为1383,如果要支持GBK则设为1386,在WIN上设为1381。