JMS in practice
Author : (丁宏亮)David
Email : hongliang.dinghl@alibaba-inc.com
Outline
§ 什么是消息服务
§ 为什么使用消息服务
§ 什么是JMS(Java Message Service)
§ JMS的通信方式
§ JMS应用程序开发步骤
§ JMS应用程序开发实践
§ 其他的JMS特性
§ Spring与JMS的集成,JMS与MDB的关系
§ JMS与Task,RMI,CORBA,WEB SERVICE的区别
什么是消息服务
1. 消息服务是支持松耦合分布式通信的对等通信机制。
2. 消息服务支持软件组件或应用程序之间松耦合分布式通信。
3.支持的数据可以是文本,数值,若是Java应用程序之间的通信,数据
可以包含对象。
4.组件或应用程序之间不一定同时运行 。
为什么使用消息服务
1.松散耦合但高内聚
2.避免直接通信
3.保证消息传递
4.异步通信
5.一对多,多对多,多对一通信
什么是JMS
JMS(Java 消息服务)是一组标准的API,能够用于访问多种消息服务器。它试图提供一套标准,统一的接口,这套接口适用于多种不同供应商的消息服务器,在这个意义上 ,它和JDBC以及JNDI是相似的。那些消息服务器一般都有自己的本机接口,用本机接口可以直接访问消息服务器。但是,使用本机接口的代码不仅可移植性差,而且每当您希望使用另一个消息服务时,都要学习新的API。而使用JMS,你可以使用一样的API访问IBM的MQSeries消息服务和 JBossMQ消息服务。准许开发者避免使用各供应商特定的API,这当然是JMS的一大 优点。
1.什么是JMS中的消息
- 消息这个术语在计算机系统中含义非常广泛,它被用来描述不同的操作系统概念,它被用来描述邮件和传真。而在这里,它指的是用于企业应用间的异步通讯。
- 这里所说的“消息”是指被企业应用而不是人所消费的异步的请求、报告以及事件。消息中包含了重要的用来系统间进行协作的信息。消息中包含了精确的数据格式以描述特定的业务活动,通过应用系统之间的消息交互,使得企业业务过程能够保持一致。
2.JMS中的消息的基本结构
- 2.1 消息头
- 2.2 消息属性
- 2.3 消息体
2.1 消息头
消息头包含标准的,必不可少的消息信息,比如每个消息都有个ID,一个优先级,一个时间戳,一个目标。这些信息大部分都在消息发送到目标之前自动设定。消息头可以通过get和 set方法存取.形式如:setJMSHeaderName,getJMSHeaderName
2.2 消息属性
消息属性是标准的,特定于供应商或特定于应用程序的可选的消息信息,主要作用是帮助消息过滤,消息属性在接收的客户机里是只读的。
2.3 消息体
消息体是实际的消息内容。JMS支持多种格式的消息体。比如:TextMessage,MapMessage,BytesMessage,StreamMessage,ObjectMessage
3.JMS由那些应用部分组成
- JMS客户端 :用来发送和接收消息的Java语言程序。
- 非JMS客户端:这些客户端是用消息系统的本地客户端API编写的,而不是JMS。如果应用先于JMS出来之前,那么它可能会既包括JMS客户端,也包括非JMS客户端。
- Messages(消息):每个应用定义了用于在客户端之间进行通讯的消息。
- JMS Provider (JMS提供者):实现了JMS规范的消息系统,该系统还提供必须的用于管理和控制全方位的功能。
- Administered Objects(被管理的对象):是预先配置的JMS对象,由系统管理员为使用JMS的客户端创建。
4.如何实现JMS客户端程序的跨平台性?
- 由于有很多JMS 消息系统,它们的底层实现技术各不相同,比如Sun MQ, IBM MQ,BEA MQ,Apache ActiveMQ,那么如何使得JMS客户端针对这些消息系统编程时能够隔离这些产品的变化而具有跨平台特性呢?那就是通过定义被管理的对象来实现。被管理的对象是由管理员通过使用JMS系统提供者的管理工具创建和定制,然后被JMS客户端使用。JMS客户端通过接口来调用这些被管理的对象,从而具备跨平台特性。
主要有两个被管理的对象:
- ConnectionFactory: 这是客户端用来创建同JMS提供者之间的连接的对象。
- Destination :这个对象是客户端用来指明消息被发送的目的地以及客户端接收消息的来源。被管理的对象一般被管理员放在JNDI名字空间中,通常在JMS客户端应用的文档中说明它所需要的JMS被管理对象,以及应以何种JNDI名字来提供这些JMS被管理对象。
下图是被管理对象的关系:
JMS的通信方式
- 传统的消息服务一般支持点对点通信和发布/订阅通信两种通信模式中的一种。JMS API对两种模式都支持。
- 1.点对点通信
- 2.发布/订阅通信
1.点对点通信
2.发布/订阅通信
JMS应用程序开发步骤
- 1.点对点JMS应用程序开发步骤
- 1.1 使用特定于服务器的机制发布目标(队列)。在JBOSS中,这是通过jbossmq-destinations-service.xml文件完成的。本质上,JNDI条目是为目标而注册的。
- 1.2 定义产生消息的客户机
- 1.3 定义接收消息的客户端
- 1.4 启动消息服务器
- 1.5 编译并启动客户端
点对点JMS应用程序模型
- 2.发布/订阅JMS应用程序开发步骤
- 1.1 使用特定于服务器的机制发布目标(主题)。
- 1.2 定义发布消息的客户机
- 1.3 定义订阅主题的客户端
- 1.4 启动消息服务器
- 1.5 编译并启动客户端
发布/订阅JMS应用程序模型
JMS API 编程模型
JMS api 类图关系
JMS应用程序开发实践
其他的JMS特性
- 1.指定消息的持久性
- 2.设置消息优先级
- 3.定义消息持续的时间
- 4.持久订阅
- 5.主题消息选择器
- 6.客户端验证
- 7.事务
Spring与JMS的集成
1.提供JMS抽象API,简化了访问目标(队列或主题)和向指定目标发布消息时JMS的使用。
2.开发人员不需要关心JMS不同版本(例如JMS 1.0.2与JMS 1.1)之间的差异。
3.开发人员不必专门处理JMS异常,因为Spring为所有JMS异常提供了一个未经检查的异常,并在JMS代码中重新抛出。
JMS与MDB的关系
由于消息驱动Bean架构在Java消息服务(JMS)技术之上。
实现接口:MssageDrivenBean和MessageListener.
JMS与Task,RMI,CORBA,WEB SERVICE的区别
- 1.Task 任务调度完成消息集成 ,使用集中消息数据库。
- 2.CORBA需要许多分布式组件的支持。
- 3.RMI 需要编写存根类。
- 4.WEB Service 门槛太高,需要UDDI,SOAP,WSDL的支持。
- 官方参考http://java.sun.com/javaee/5/docs/tutorial/doc/bncdq.html
代码示例:
消息发送端:
- package com.alisoft.demo;
- import java.util.Properties;
- import javax.jms.JMSException;
- import javax.jms.Queue;
- import javax.jms.QueueConnection;
- import javax.jms.QueueConnectionFactory;
- import javax.jms.QueueSender;
- import javax.jms.QueueSession;
- import javax.jms.Session;
- import javax.jms.TextMessage;
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- public class QueueWrite {
- private QueueConnection qc=null;
- private Context context=null;
- public static void main(String args[]){
- QueueWrite queueWrite=new QueueWrite();
- queueWrite.init();
- queueWrite.sendMessage();
- }
- public void init(){
- try {
- System.out.println("...init context begin...");
- Properties env=new Properties();
- env.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
- env.put(Context.PROVIDER_URL,"jnp://127.0.0.1:1099");
- env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming");
- context=new InitialContext(env);
- QueueConnectionFactory qcf=(QueueConnectionFactory)context.lookup("ConnectionFactory");
- qc=qcf.createQueueConnection();
- System.out.println("...init context end...");
- } catch (NamingException e) {
- e.printStackTrace();
- } catch (JMSException e) {
- e.printStackTrace();
- }
- }
- public void sendMessage(){
- try {
- QueueSession qs=qc.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
- Queue q=(Queue)context.lookup("queue/B");
- QueueSender sender=qs.createSender(q);
- TextMessage message=qs.createTextMessage();
- for(int i=0;i<100;i++){
- message.setText("www.alisoft.com--->"+i);
- sender.send(q,message);
- }
- qc.close();
- } catch (JMSException e) {
- e.printStackTrace();
- } catch (NamingException e) {
- e.printStackTrace();
- }
- }
- }
消息接收端:
- package com.alisoft.demo;
- import java.util.Properties;
- import javax.jms.JMSException;
- import javax.jms.Message;
- import javax.jms.Queue;
- import javax.jms.QueueConnection;
- import javax.jms.QueueConnectionFactory;
- import javax.jms.QueueReceiver;
- import javax.jms.QueueSession;
- import javax.jms.Session;
- import javax.jms.TextMessage;
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.naming.NamingException;
- public class QueueRead {
- private QueueConnection qc=null;
- private Context context=null;
- public static void main(String args[]){
- QueueRead queueRead=new QueueRead();
- queueRead.initContext();
- queueRead.ReceiveMessage();
- }
- public void initContext(){
- try {
- System.out.println("...init context begin...");
- Properties env=new Properties();
- env.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
- env.put(Context.PROVIDER_URL,"jnp://127.0.0.1:1099");
- env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming");
- context=new InitialContext(env);
- QueueConnectionFactory qcf=(QueueConnectionFactory)context.lookup("ConnectionFactory");
- qc=qcf.createQueueConnection();
- System.out.println("...init context end...");
- } catch (NamingException e) {
- e.printStackTrace();
- } catch (JMSException e) {
- e.printStackTrace();
- }
- }
- public String ReceiveMessage(){
- StringBuffer sb=new StringBuffer();
- TextMessage text=null;
- try {
- QueueSession qs=qc.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
- Queue q=(Queue)context.lookup("queue/B");
- QueueReceiver qr=qs.createReceiver(q);
- qc.start();
- Message m=qr.receive(1);
- while(m!=null){
- if (m instanceof TextMessage){
- text=(TextMessage)m;
- System.out.println("message:"+text.getText());
- }
- sb.append(text.getText()+"/r/n");
- m=qr.receive(1);
- }
- } catch (JMSException e) {
- e.printStackTrace();
- } catch (NamingException e) {
- e.printStackTrace();
- }
- System.out.println(sb.toString());
- return sb.toString();
- }
- }