消息中间件原理及JMS简介之二
作者:orangelizq
2.3 消息中间件的传递模式
消息中间件一般有两种传递模型:点对点模型(PTP)和发布-订阅模型(Pub/Sub)[2]。
1. 点对点模型(PTP)
点对点模型用于消息生产者和消息消费者之间点到点的通信。消息生产者将消息发动到由某个名字标识的特定消费者。这个名字实际上对应于消息服务中的一个队列(Queue),在消息传动给消费者之前它被存储在这个队列中。队列可以是持久的,以保证在消息服务出现故障时仍然能够传递消息。
2. 发布-订阅模型(Pub/Sub)
发布-订阅模型用称为主题(topic)的内容分层结构代替了PTP模型中的惟一目的地,发送应用程序发布自己的消息,指出消息描述的是有关分层结构中的一个主题的信息。希望接收这些消息的应用程序订阅了这个主题。订阅包含子主题的分层结构中的主题的订阅者可以接收该主题和其子主题发表的所有消息。
下图展示了发布和订阅模型:[2]
多个应用程序可以就一个主题发布和订阅消息,而应用程序对其他人仍然是匿名的。MOM 起着代理(broker)的作用,将一个主题已发表的消息路由给该主题的所有订阅者。
2.4 消息中间件产品与JMS
从上个世纪90年代初,随着不同厂商消息中间件大量上市,消息中间件技术得到了长足的发展。目前,IBM和BEA的中间件产品在银行、证券、电信等高端行业,以及IT等行业中得到广泛应用。IBM凭借其在1999年推出的应用服务器WebSphere,扎根金融、证券等行业,在超大型以及系统整合型应用方面优势突出;BEA则是专门从事中间件开发的公司,它的应用服务器WebLogic在美国市场占有率超过60%,在国内电信及证券行业占据主要地位;Sun、Oracle、Sybase和Borland等厂商也都有自己的应用服务器;近年来,以金蝶、东方通等公司为代表的国产中间件产品也发展迅速。[3]
由于没有统一的规范和标准,基于消息中间件的应用不可移植,不同的消息中间件也不能互操作,这大大阻碍了消息中间件的发展。 Java Message Service(JMS, Java消息服务)是SUN及其伙伴公司提出的旨在统一各种消息中间件系统接口的规范。它定义了一套通用的接口和相关语义,提供了诸如持久、验证和事务的消息服务,它最主要的目的是允许Java应用程序访问现有的消息中间件。JMS规范没有指定在消息节点间所使用的通讯底层协议,来保证应用开发人员不用与其细节打交道,一个特定的JMS实现可能提供基于TCP/IP、HTTP、UDP或者其它的协议。
目前许多厂商采用并实现了JMS API,现在,JMS产品能够为企业提供一套完整的消息传递功能,下面是一些比较流行的JMS商业软件和开源产品。
1.IBM MQSeries
IBM MQ系列产品提供的服务使得应用程序可以使用消息队列进行相互交流,通过一系列基于Java的API,提供了MQSeries在Java中应用开发的方法。它支持点到点和发布/订阅两种消息模式,在基本消息服务的基础上增加了结构化消息类,通过工作单元提供数据整合等内容。
2.WebLogic
WebLogic是BEA公司实现的基于工业标准的J2EE应用服务器,支持大多数企业级JavaAPI,它完全兼容JMS规范,支持点到点和发布/订阅消息模式,它具有以下一些特点:
1) 通过使用管理控制台设置JMS配置信息;
2) 支持消息的多点广播;
3) 支持持久消息存储的文件和数据库;
4) 支持XML消息,动态创建持久队列和主题。
3.SonicMQ
SonicMQ是Progress公司实现的JMS产品。除了提供基本的消息驱动服务之外,SonicMQ也提供了很多额外的企业级应用开发工具包,它具有以下一些基本特征:
1) 提供JMS规范的完全实现,支持点到点消息模式和发布/订阅消息模式;
2) 支持层次安全管理;
3) 确保消息在Internet上的持久发送;
4) 动态路由构架(DRA)使企业能够通过单个消息服务器动态的交换消息;
5) 支持消息服务器的集群。
4.Active MQ
Active MQ是一个基于Apcache 2.0 licenced发布,开放源码的JMS产品。其特点为:
1) 提供点到点消息模式和发布/订阅消息模式;
2) 支持JBoss、Geronimo等开源应用服务器,支持Spring框架的消息驱动;
3) 新增了一个P2P传输层,可以用于创建可靠的P2P JMS网络连接;
4) 拥有消息持久化、事务、集群支持等JMS基础设施服务。
5.OpenJMS
OpenJMS是一个开源的JMS规范的实现,它包含以下几个特征:
1) 它支持点到点模型和发布/订阅模型;
2) 支持同步与异步消息发送;
3) 可视化管理界面,支持Applet;
4) 能够与Jakarta Tomcat这样的Servlet容器结合;
5) 支持RMI、TCP、HTTP与SSL协议。
三、消息中间件应用之JMS
3.1 JMS简介
Java Message Service 规范 1.1 声称:JMS 是一组接口和相关语义,它定义了 JMS 客户如何访问企业消息产品的功能。
在 JMS 之前,每一家 MOM 厂商都用专有 API 为应用程序提供对其产品的访问,通常可用于许多种语言,其中包括 Java 语言。JMS 通过 MOM 产品为 Java 程序提供了一个发送和接收消息的标准的、便利的方法。用 JMS 编写的程序可以在任何实现 JMS 标准的 MOM 上运行。
JMS 可移植性的关键在于:JMS API 是由 Sun 作为一组接口而提供的。提供了 JMS 功能的产品是通过提供一个实现这些接口的提供者来做到这一点的。开发人员可以通过定义一组消息和一组交换这些消息的客户机应用程序建立 JMS 应用程序。[4]
JMS1.0版本于1998年推出,最新的版本是2002发布的JMS 1.1规范。JMS支持消息中间件的两种传递模式:点到点模式和发布-订阅模式。在JMS 1.1以前的版本中,每一种都有自己的特定于该模式的一组客户机接口。JMS1.1版本提供了单一的一组接口,它允许客户机可以在两个模式中发送和接收消息。这些“模式无关的接口”保留了每一个模式的语义和行为,是实现 JMS 客户机的最好选择。
统一模式的好处是:
1) 使得用于客户机的编程更简单。
2) 队列和主题的操作可以是同一事务的一部分。
3) 为JMS提供者提供了优化其实现的机会。
3.2 JMS体系结构
3.2.1 JMS接口描述
JMS 支持两种消息类型PTP 和Pub/Sub,分别称作:PTP Domain 和Pub/Sub Domain,这两种接口都继承统一的JMS Parent 接口,JMS 主要接口如下所示:
JMS Parent |
PTP Domain |
Pub/Sub Domain |
ConnectionFactory |
QueueConnectionFactory |
TopicConnectionFactory |
Connection |
QueueConnection |
TopicConnection |
Destination |
Queue |
Topic |
Session |
QueueSession |
TopicSession |
MessageProducer |
QueueSender |
TopicPublisher |
MessageConsumer |
QueueReceiver |
TopicSubscriber |
以下是对这些接口的简单描述:
ConnectionFactory:连接工厂,JMS 用它创建连接
Connection:JMS 客户端到JMS Provider 的连接
Destination:消息的目的地
Session:一个发送或接收消息的线程
MessageProducer: 由Session 对象创建的用来发送消息的对象
MessageConsumer: 由Session 对象创建的用来接收消息的对象
3.2.2 JMS消息模型
JMS 消息由以下几部分组成:消息头,属性,消息体。[4]
l 消息头(header):JMS消息头包含了许多字段,它们是消息发送后由JMS提供者或消息发送者产生,用来表示消息、设置优先权和失效时间等等,并且为消息确定路由。
l 属性(property):由消息发送者产生,用来添加删除消息头以外的附加信息。
l 消息体(body):由消息发送者产生,JMS中定义了5种消息体:ByteMessage、MapMessage、ObjectMessage、StreamMessage和TextMessage。
3.3 JMS编程实践
广义上说,一个JMS应用是几个JMS 客户端交换消息,开发JMS客户端应用由以下几步构成:[2]
1) 用JNDI 得到ConnectionFactory对象;
2) 用JNDI 得到目标队列或主题对象,即Destination对象;
3) 用ConnectionFactory创建Connection 对象;
4) 用Connection对象创建一个或多个JMS Session;
5) 用Session 和Destination 创建MessageProducer和MessageConsumer;
6) 通知Connection 开始传递消息。
3.3.1 消息生产者编程
消息生产者程序如下:[2]
package org.jms.test;
import java.io.*;
import javax.jms.*;
import javax.naming.*;
public class Sender {
public static void main(String[] args) {
new Sender().send();
}
public void send() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
//Prompt for JNDI names
System.out.println("Enter ConnectionFactory name:");
String factoryName = reader.readLine();
System.out.println("Enter Destination name:");
String destinationName = reader.readLine();
//Look up administered objects
InitialContext initContext = new InitialContext();
ConnectionFactory factory =
(ConnectionFactory) initContext.lookup(factoryName);
Destination destination = (Destination) initContext.lookup(destinationName);
initContext.close();
//Create JMS objects
Connection connection = factory.createConnection();
Session session =
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer sender = session.createProducer(queue);
//Send messages
String messageText = null;
while (true) {
System.out.println("Enter message to send or 'quit':");
messageText = reader.readLine();
if ("quit".equals(messageText))
break;
TextMessage message = session.createTextMessage(messageText);
sender.send(message);
}
//Exit
System.out.println("Exiting...");
reader.close();
connection.close();
System.out.println("Goodbye!");
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
3.3.2 消息消费者编程
消息消费者程序如下:[2]
package compute;
import java.io.*;
import javax.jms.*;
import javax.naming.*;
public class Receiver implements MessageListener {
private boolean stop = false;
public static void main(String[] args) {
new Receiver().receive();
}
public void receive() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
//Prompt for JNDI names
System.out.println("Enter ConnectionFactory name:");
String factoryName = reader.readLine();
System.out.println("Enter Destination name:");
String destinationName = reader.readLine();
reader.close();
//Look up administered objects
InitialContext initContext = new InitialContext();
ConnectionFactory factory =
(ConnectionFactory) initContext.lookup(factoryName);
Destination destination = (Destination) initContext.lookup(destinationName);
initContext.close();
//Create JMS objects
Connection connection = factory.createConnection();
Session session =
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer receiver = session.createConsumer(queue);
receiver.setMessageListener(this);
connection.start();
//Wait for stop
while (!stop) {
Thread.sleep(1000);
}
//Exit
System.out.println("Exiting...");
connection.close();
System.out.println("Goodbye!");
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void onMessage(Message message) {
try {
String msgText = ((TextMessage) message).getText();
System.out.println(msgText);
if ("stop".equals(msgText))
stop = true;
} catch (JMSException e) {
e.printStackTrace();
stop = true;
}
}
}
以上程序都较为简单,基本上为自解释的。
四、消息中间件总结
消息中间件自从产生以来发展迅速,在分布式联机事务处理环境中,它担当通讯资源管理器(CRM)的角色,为分布式应用提供实时、高效、可靠的、跨越不同操作系统、不同网络的消息传递服务,同时消息中间件减少了开发跨平台应用程序的复杂性。在要求可靠传输的系统中可以利用消息中间件作为一个通讯平台,向应用提供可靠传输功能来传递消息和文件。
参考文献
[1] 李华琰、郭英奎 著:Java 中间件开发技术 . 中国水利水电出版社,2005
[2] JMS在线教程 . 2004 http://www6.software.ibm.com/developerworks/cn/education/java/j-jms
[3] 基于JMS的消息中间件的研究和设计 . 姚刚. 2006
[4] 消息中间件和JMS . 2005. http://www.huihoo.org/jfox/jfoxmq/mom_jms.html
[5] 基于JMS的数据汇集系统的研究与实现. 2006.
http://www.cndw.com/tech/program/2006042760285.asp
Author: orangelizq
email: [email protected]
|
|