ActiveMQ - JMS介绍

一、概述  

  Message,即消息。人与人之间通过消息传递信息。言语、眼神、肢体动作都可被视为消息体。当然还有我们经常用到的邮件、短信。计算机系统也由消息来主导运行。每一条指令的执行,每一个数据包的传递。软件系统间的合作也不例外,消息告诉各个系统应该怎样协作。事件处理机制,也是消息传送的过程。消息无处不在。 

  消息分为同步消息和异步消息。同步消息在接收到对方的返回前,需要挂起,直到返回或超时。异步消息只需要发送消息,不需要对方系统的立即反馈。 

  同步消息如java RPC调用,同步调用依赖于被调用方,如果被调用方失败或网络错误,那么程序就没办法继续执行下去,造成一个系统最薄弱的环节依赖于对方系统。而多个系统通过同步调用方式耦合在一起的时候,那么可靠性取决于最薄弱的一方系统。而异步调用能增强一个系统的健壮性。当然,不是任何情况都适合异步调用,还是那句话,能异步的地方,尽量异步。 

  异步消息,如同一个邮箱系统,我们把信件丢入邮桶,邮递员会更具上面的地址,送达到这封信要去的地方。邮箱和信件格式由邮局提供定义,比如邮箱需要有一个口子投递邮件,邮件需要有地址,邮政编码等等。而这些邮箱具体的加工和制作均交由各自的厂商来完成。 
在java消息领域,我们也有一个称为消息中间件的东西,来提供这样一个服务。消息的发送、消费接口、消息体的格式等都由JMS来定义,而具体的实现由各个消息中间件厂商来实现。JMS是sun公司对于消息中间件的一个规范。对java领域里的消息起到举足轻重的作用。以前的消息交互,均各自实现一套格式,如同一个国家的人都用不同的方言跟另外来自不同省份的人交流一样。自从规范了普通话,我们的交流成本降低了。这也正如JMS规范在整个java消息领域的作用。 

二、JMS简介  

  JMS1.1规范定义了一些概念和一组API,可以使得在我们利用消息系统的过程中,不依赖于各个厂商的具体实现,便能写出消息代码。由于不依赖具体厂商实现,这样的代码有很好的移植性。 
JMS1.1定义了的部分概念: 
1、JMS客户端:接收或发送消息的java系统 
2、JMS消息体:系统间发送的消息体 
3、JMS提供商:JMS规范实现厂商 
4、JMS管理对象:预先配置好的用于JMS客户端的JMS对象。如ConnectionFactory跟JMS服务端的连接工厂,Destination 接收和发送消息的目标地址等 
5、JMS Domain:JMS定义了两种域模型,一种是PTP(point-to-point)即点对点消息传输模型,一种是pub/sub(publish-subscribe)即发布订阅模型。 
  PTP通过一个先进先出的queue实现。很多人在这个PTP概念上有所误解,所谓点对点不是指生产者和消费者只有一个。PTP如下图所示: 



  我们可以看到,一个或多个生产者发送消息,消息m2先抵达了queue,然后m1也发出了,并一同存在于一个先进先出的queue里面。消费者也存在一个或多个,对queue里的消息进行消费。但消息被随机的一个消费者消费且仅消费一次。 

在pub/sub消息模型中,消息被广播给所有订阅者。如下图: 




JMS1.1定义了如下接口: 


JMS通用接口JMS PTP接口JMS pub/sub 接口描述
ConnectionFactoryQueueConnectionFactoryTopicConnectionFactory创建与JMS服务的连接
ConnectionQueueConnectionTopicConnectionJMS服务的连接,可创建Session
DestinationQueueTopic发送接收消息的目标,Queue和Topic均可视为一种Destination
SessionQueueSessionTopicSessionSession
MessageProducerQueueSenderTopicPublisher消息发送者
MessageConsumerQueueReceiver、QueueBrowserTopicSubscriber消息接收者/浏览者/订阅者



接口关系如下: 


 


  可以看到一个JMS应用的发送端的标准流程是:创建连接工厂>创建连接>创建session>创建发送者>创建消息体>发送消息到Destination(queue或topic)。 

  接收端则为:创建连接工厂>创建连接>创建session>创建接收者>创建消息监听器监听某Destination的消息>获取消息并执行业务逻辑 

下面展示了JMS的编码模板: 

发送端: 

   
Java代码   收藏代码
  1. public class QueueSender {  
  2.         public static void main(String[] args) throws JMSException{  
  3.             ConnectionFactory factory=new ActiveMQConnectionFactory("tcp://localhost:61616");//这里为简便,依赖ActiveMQ的JMS实现。现实程序中可以通过jndi查找方式查找factory,这样彻底避免关联特定JMS实现者。并拥有最大的可移植性。  
  4.             Connection connection=factory.createConnection();  
  5.             Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);  
  6.             Message message=session.createTextMessage("Hello World!"+new Date().getTime());  
  7.             Queue queue=new ActiveMQQueue("queue.somebody");//同上,这里依赖了ActiveMQ的JMS实现,现实中可以采用jndi查找方式获得Queue。  
  8.             MessageProducer producer=session.createProducer(queue);  
  9.             producer.send(message);  
  10.             session.close();  
  11.             connection.close();  
  12.         }  
  13.     }  


接收端: 
       
Java代码   收藏代码
  1. public class QueueReceiver {  
  2.             public static void main(String[] args) throws JMSException{  
  3.                 ConnectionFactory factory=new ActiveMQConnectionFactory("tcp://localhost:61616");  
  4.                 Connection connection=factory.createConnection();  
  5.                 Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);  
  6.                 Queue queue=new ActiveMQQueue("queue.somebody");  
  7.                 MessageConsumer receiver=session.createConsumer(queue);//创建两个接收者,同时消费同一个queue的消息。queue里的消息派发且仅派发一次给唯一一个消费者。  
  8.                 MessageConsumer receiver2=session.createConsumer(queue);  
  9.                 receiver.setMessageListener(new QueueMessageListener("1"));  
  10.                 receiver2.setMessageListener(new QueueMessageListener("2"));  
  11.                 connection.start();  
  12.             }  
  13.         }  


消息侦听者: 
   
Java代码   收藏代码
  1. public class QueueMessageListener implements MessageListener{  
  2.     private String num;  
  3.     public QueueMessageListener(String num){  
  4.         super();  
  5.         this.num=num;  
  6.     }  
  7.     public QueueMessageListener(){  
  8.         super();  
  9.     }  
  10.     public void onMessage(Message message) {  
  11.         System.out.println(message.toString()+num);  
  12.     }  


  我们安装了ApacheMQ之后,启动本地MQ服务,便能测试这几个类的执行方式,观察执行过程。我们可以发现消息仅被消费了一次,JMS提供商必须保证一条消息不会重复被消费,当然更不能丢失。 

  在PTP下,我们建立一个queue以后,如果没有消费者连入该queue,那么消息生产者发送的消息将堵塞在queue里面,直到有消费者来消费。如果消费者出现故障,不再连入queue,消息还会保存在queue里面,等待下次消费者的连入,再进行消费。在pub/sub模型下,如果消费者出现故障,那么所有的消息,将不在为之保留,下次连接之时,只能消费此时生产者发送的消息。JMS于是定义了另一种订阅者,叫Durable Subscription(持久订阅者),该订阅者在第一次连上topic后,就注册了该topic的消息,消息会在消费者不可用的情况下为之保留消息,并在其再次连上topic后,重推消息给消费者。 

  理解了JMS中的两种消息模型以后,我们来说说JMS中的消息体格式。JMS对消息体定为三个部分 1)head 消息头信息 2)properties 消息属性值 3)body 消息内容。如下图所示: 

 

其含义大致解释如下: 

头类型描述设置方
JMSDestination描述该消息发往的目的地在发送方法中设定
JMSDeliveryModeNON_PERSISTENT 非持久化 表示消息发往JMS消息服务器之后,保存在内存中,不做持久化;PERSISTENT 持久化 消息发往JMS消息服务器之后,持久化数据。以保证消息服务器拓机造成的消息丢失发送方法中设定
JMSExpiration消息过期时间。消费者在发送消息时,可设定消息的time-to-live时间,如producer.setTimeToLive(10000),在消息发送后,该时间保留在消息的JMSExpiration字段中,如果在指定的这段时间内消息未被消费,该消息将会被丢弃。在发送方法中设定
JMSPriority消息优先级,JMS把消息分为10个等级,0-4为普通优先级,5-9为加快优先级,ActiveMQ中默认的消息优先级为4在发送方法中设定
JMSMessageId消息唯一性ID,必须以“ID:”为前缀在发送方法中设定
JMSTimestamp消息发送时间,表示消息的发送时间点,而非传送时间在发送方法中设定
JMSCorrelationID 客户端
JMSReplyTo 客户端
JMSType 客户端
JMSRedelivered JMS提供商
属性值含义 
properties  
消息体含义 
body消息体分为1:StreamMessage2:MapMessage 3:TextMessage 4:ObjectMessage 5:BytesMessage 


再谈消息可靠性  

    在上面,谈及消息体格式定义中,有个字段 JMSDeliveryMode用来表示该消息发送后,JMS提供商应该怎么处理消息。PERSISTENT(持久化)的消息在JMS服务器中持久化。接收端如果采用点对点的queue方式或者Durable Subscription(持久订阅者)方式,那么消息可保证只且只有一次被成功接收。NON_PERSISTENT(非持久化)的消息在JMS服务器关闭或宕机时,消息丢失。根据发送端和接收端采用的方式,列出如下可靠性表格,以作参考。 

引用
注意:以下的可靠性不包括JMS服务器由于资源关系,造成的消息不能持久化等因素引起的不可靠(该类不可靠应该是JMS提供商或硬件引起的的资源和处理能力的极限问题,应该由管理人员解决)。也不包括消息由于超时时间造成的销毁丢失。

消息发送端消息接收端可靠性及因素
PERSISTENTqueue receiver/durable subscriber消费一次且仅消费一次。可靠性最好,但是占用服务器资源比较多。
PERSISTENTnon-durable subscriber最多消费一次。这是由于non-durable subscriber决定的,如果消费端宕机或其他问题导致与JMS服务器断开连接,等下次再联上JMS服务器时的一系列消息,不为之保留。
NON_PERSISTENTqueue receiver/durable subscriber最多消费一次。这是由于服务器的宕机会造成消息丢失
NON_PERSISTENTnon-durable subscriber最多消费一次。这是由于服务器的宕机造成消息丢失,也可能是由于non-durable subscriber的性质所决定


消息的通知确认  
在客户端接收了消息之后,JMS服务怎样有效确认消息是否已经被客户端接收呢?Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);这段代码创建一个非事务性的session,并采用auto_acknowledge方式通知JMS服务器。如果采用事务性session时,通知会伴随session的commit/rollback同时发送通知。在我们采用非事务session时,有三种通知方式。 
通知方式效果
DUPS_OK_ACKNOWLEDGEsession延迟通知。如果JMS服务器宕机,会造成重复消息的情况。程序必须保证处理重复消息而不引起程序逻辑的混乱。
AUTO_ACKNOWLEDGE当receive或MessageListener方法成功返回后自动通知。
CLIENT_ACKNOWLEDGE客户端调用消息的acknowledge方法通知


三、MQ、JMS以及ActiveMQ 关系的理解



  1. ms 的一个标准或者说是一个协议.  通常用于企业级应用的消息传递. 主要有topic 消息(1 对多), queue 消息(11).  

  2. activemq 是一个jms 的实现, apache 出的.   

  3. 另外还其它的实现 jboss 啦啥的...  

MQ简介:

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBMWEBSPHERE MQ。

MQ特点:

MQ的消费-生产者模型的一个典型的代表,一端往消息队列中不断的写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和JMS类似,但不同的是JMS是SUN JAVA消息中间件服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。

使用场景:

在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。

JMS简介:

JMS即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。

定义:

JMS(Java Messaging Service)是Java平台上有关面向消息中间件(MOM)的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发,翻译为Java消息服务。

简介:

JMS是一种与厂商无关的 API,用来访问消息收发系统消息。它类似于JDBC(Java DatabaseConnectivity):这里,JDBC 是可以用来访问许多不同关系数据库的 API,而 JMS 则提供同样与厂商无关的访问方法,以访问消息收发服务。许多厂商目前都支持JMS,包括 IBM 的 MQSeries、BEA的 Weblogic JMS service和 Progress 的 SonicMQ,这只是几个例子。 JMS 使您能够通过消息收发服务(有时称为消息中介程序或路由器)从一个 JMS 客户机向另一个JMS客户机发送消息。消息是 JMS 中的一种类型对象,由两部分组成:报头和消息主体。报头由路由信息以及有关该消息的元数据组成。消息主体则携带着应用程序的数据或有效负载。根据有效负载的类型来划分,可以将消息分为几种类型,它们分别携带:简单文本(TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),还有无有效负载的消息 (Message)。

JMS和MQ的关系:

JMS是一个用于提供消息服务的技术规范,它制定了在整个消息服务提供过程中的所有数据结构和交互流程。而MQ则是消息队列服务,是面向消息中间件(MOM)的最终实现,是真正的服务提供者;MQ的实现可以基于JMS,也可以基于其他规范或标准。

支持JMS的开源MQ:

目前选择的最多的是ActiveMQ

ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

主要特点:

1. 多种语言和协议编写客户端。语言: Java, C, C++, C#, Ruby, Perl, Python, PHP。应用协议: OpenWire,Stomp REST,WSNotification,XMPP,AMQP

2. 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)

3. 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性

4. 通过了常见J2EE服务器(如 Geronimo,JBoss 4, GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上

5. 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA

6. 支持通过JDBC和journal提供高速的消息持久化

7. 从设计上保证了高性能的集群,客户端-服务器,点对点

8. 支持Ajax

9. 支持与Axis的整合

10. 可以很容易得调用内嵌JMS provider,进行测试

11. ActiveMQ速度非常快;一般要比jbossMQ快10倍。

优点:是一个快速的开源消息组件(框架),支持集群,同等网络,自动检测,TCP,SSL,广播,持久化,XA,和J2EE1.4容器无缝结合,并且支持轻量级容器和大多数跨语言客户端上的Java虚拟机。消息异步接受,减少软件多系统集成的耦合度。消息可靠接收,确保消息在中间件可靠保存,多个消息也可以组成原子事务。

缺点:ActiveMQ默认的配置性能偏低,需要优化配置,但是配置文件复杂,ActiveMQ本身不提供管理工具;示例代码少;主页上的文档看上去比较全面,但是缺乏一种有效的组织方式,文档只有片段,用户很难由浅入深进行了解,二、文档整体的专业性太强。在研究阶段可以通过查maillist、看Javadoc、分析源代码来了解。

ActiveMQ应用场景:

1、 不同语言应用集成

ActiveMQ 中间件用Java语言编写,因此自然提供Java客户端 API。但是ActiveMQ 也为C/C++、.NET、Perl、PHP、Python、Ruby 和一些其它语言提供客户端。在你考虑如何集成不同平台不同语言编写应用的时候,ActiveMQ 拥有巨大优势。在这样的例子中,多种客户端API通过ActiveMQ 发送和接受消息成为可能,无论使用的是什么语言。此外,ActiveMQ 还提供交叉语言功能,该功能整合这种功能,无需使用远程过程调用(RPC)确实是个优势,因为消息协助应用解耦。

2、 作为RPC的替代

使用RPC同步调用的应用十分普遍。假设大多数客户端服务器应用使用RPC,包括ATM、大多数WEB应用、信用卡系统、销售点系统等等。尽管很多系统很成功,但是转换使用异步消息可以带来很多好处,而且也不会放弃响应保证。使用同步请求的系统在规模上有较大的限制,因为请求会被阻塞,从而导致整个系统变慢。如果使用异步消息替代,可以很容易增加额外的消息接收者,使得消息能被并发消耗,从而加快请求处理。当然,你的系统应用间应该是解耦的。

3、 应用之间解耦

正如之前讨论的,紧耦合架构可以导致很多问题,尤其是如果他们是分布的。松耦合架构,在另一方面,证实了更少的依赖性,能够更好地处理不可预见的改变。不仅可以在系统中改变组件而不影响整个系统,而且组件交互也相当的简单。相比使用同步的系统(调用者必须等待被调用者返回信息),异步系统(调用方发送消息后就不管,即fire-and-forget)能够给我们带来事件驱动架构(event-driven architecture EDA)。

4、 作为事件驱动架构的主干

解耦,异步架构的系统允许通过代理器自己配置更多的客户端,内存等(即vertical scalability)来扩大系统,而不是增加更多的代理器(即horizontal scalability)。考虑如亚马逊这样繁忙的电子商务系统。当用户购买物品,事实上系统需要很多步骤去处理,包括下单,创建发票,付款,执行订单,运输等。但是用户下单后,会立即返回“谢谢你下单”的界面。不只是没有延迟,而且用户还会受到一封邮件表明订单已经收到。在亚马逊下单的例子就是一个多步处理的例子。每一步都由单独的服务去处理。当用户下单是,有一个同步的体积表单动作,但整个处理流程并不通过浏览器同步处理。相反地,订单马上被接受和反馈。而剩下的步骤就通过异步处理。如果在处理过程中出错,用户会通过邮件收到通知。这样的异步处理能提供高负载和高可用性。

5、 提高系统扩展性

很多使用事件驱动设计的系统是为了获得高可扩展性,例如电子商务,政府,制造业,线上游戏等。通过异步消息分开商业处理步骤给各个应用,能够带来很多可能性。考虑设计一个应用来完成一项特殊的任务。这就是面向服务的架构(service-oriented architecture SOA)。每一个服务完成一个功能并且只有一个功能。应用就通过服务组合起来,服务间使用异步消息和最终一致性。这样的设计便可以引入一个复杂事件处理概念(complex event processing CEP)。使用CEP,部件间的交互可以被记录追踪。在异步消息系统中,可以很容易在部件间增加一层处理。

其他开源JMS供应商;

jbossmq(jboss 4)

jboss messaging (jboss 5)

joram-4.3.21 2006-09-22

openjms-0.7.7-alpha-3.zipDecember 26,2005

mantamq

ubermq

SomnifugiJMS 2005-7-27

开源的JMSProvider大部分都已经停止发展了,剩下的几个都是找到了东家,和某种J2EE 服务器挂钩,比如jbossmq 与jboss,joram与jonas(objectweb组织),ActiveMQ 与Geronimo(ASF APACHE基金组织),而在这3个之间,从网络底层来看,只有ActiveMQ使用了NIO,单从这个角度来看ActiveMQ在性能上会有一定的优势。



JMS就先简要的介绍到这里,详细资料请参看JMS1.1规范:http://www.open-open.com/doc/view/aa41a61492b445b9bc69204eccecf26d


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值