1.概述

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

ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现

2.预期读者

  1. 精通Java SEJava EE技术。

  2. 熟悉JMS规范。

  3. 参加过一个以上大中型项目开发。

3.名称解释

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

4.JMS规范介绍

 4.1.JMS的基本构件 

  4.1.1.连接工厂

 连接工厂是客户用来创建连接的对象,例如ActiveMQ提供的ActiveMQConnectionFactory。

4.1.2.连接

JMSConnection封装了客户与JMS提供者之间的一个虚拟的连接。

4.1.3.会话

JMSSession是生产和消费消息的一个单线程上下文。会话用于创建消息生产者(producer)、消息消费者(consumer)和消息(message)等。会话提供了一个事务性的上下文,在这个上下文中,一组发送和接收被组合到了一个原子操作中。

4.1.4.目的地

目的地是客户用来指定它生产的消息的目标和它消费的消息的来源的对象。JMS1.0.2规范中定义了两种消息传递域:点对点(PTP)消息传递域和发布/订阅消息传递域。

点对点消息传递域的特点如下:

每个消息只能有一个消费者。

消息的生产者和消费者之间没有时间上的相关性。无论消费者在生产者发送消息的时候是否处于运行状态,它都可以提取消息。

发布/订阅消息传递域的特点如下:

每个消息可以有多个消费者。

生产者和消费者之间有时间上的相关性。订阅一个主题的消费者只能消费自它订阅之后发布的消息。JMS规范允许客户创建持久订阅,这在一定程度上放松了时间上的相关性要求。持久订阅允许消费者消费它在未处于激活状态时发送的消息。

  在点对点消息传递域中,目的地被成为队列(queue);在发布/订阅消息传递域中,目的地被成为主题(topic)。

4.1.5.消息生产者

消息生产者是由会话创建的一个对象,用于把消息发送到一个目的地。

4.1.6.消息消费者

消息消费者是由会话创建的一个对象,它用于接收发送到目的地的消息。消息的消费可以采用以下两种方法之一:

同步消费。通过调用消费者的receive方法从目的地中显式提取消息。receive方法可以一直阻塞到消息到达。

异步消费。客户可以为消费者注册一个消息监听器,以定义在消息到达时所采取的动作。

4.1.7.消息

JMS消息由以下三部分组成:

消息头。每个消息头字段都有相应的getter和setter方法。

消息属性。如果需要除消息头字段以外的值,那么可以使用消息属性。

消息体。JMS定义的消息类型有TextMessage、MapMessage、BytesMessage、StreamMessage和ObjectMessage。

 4.2.JMS的可靠性机制

4.2.1.确认

JMS消息只有在被确认之后,才认为已经被成功地消费了。消息的成功消费通常包含三个阶段:客户接收消息、客户处理消息和消息被确认。

在事务性会话中,当一个事务被提交的时候,确认自动发生。在非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement mode)。该参数有以下三个可选值:

Session.AUTO_ACKNOWLEDGE。当客户成功的从receive方法返回的时候,或者从MessageListener.onMessage方法成功返回的时候,会话自动确认客户收到的消息。

Session.CLIENT_ACKNOWLEDGE。 客户通过消息的acknowledge方法确认消息。需要注意的是,在这种模式中,确认是在会话层上进行:确认一个被消费的消息将自动确认所有已被会话消费的消息。例如,如果一个消息消费者消费了10个消息,然后确认第5个消息,那么所有10个消息都被确认。

Session.DUPS_ACKNOWLEDGE。 该选择只是会话迟钝第确认消息的提交。如果JMSprovider失败,那么可能会导致一些重复的消息。如果是重复的消息,那么JMS provider必须把消息头的JMSRedelivered字段设置为true。

4.2.2.持久性

JMS支持以下两种消息提交模式:

PERSISTENT。指示JMS provider持久保存消息,以保证消息不会因为JMS provider的失败而丢失。

NON_PERSISTENT。不要求JMS provider持久保存消息。

4.2.3.优先级

可以使用消息优先级来指示JMSprovider首先提交紧急的消息。优先级分10个级别,从0(最低)到9(最高)。如果不指定优先级,默认级别是4。需要注意的是,JMS provider并不一定保证按照优先级的顺序提交消息。

4.2.4.消息过期

可以设置消息在一定时间后过期,默认是永不过期。

4.2.5.临时目的地

可以通过会话上的createTemporaryQueue方法和createTemporaryTopic方法来创建临时目的地。它们的存在时间只限于创建它们的连接所保持的时间。只有创建该临时目的地的连接上的消息消费者才能够从临时目的地中提取消息。

4.2.6.持久订阅

首先消息生产者必须使用PERSISTENT提交消息。客户可以通过会话上的createDurableSubscriber方法来创建一个持久订阅,该方法的第一个参数必须是一个topic。第二个参数是订阅的名称。

JMSprovider会存储发布到持久订阅对应的topic上的消息。如果最初创建持久订阅的客户或者任何其它客户使用相同的连接工厂和连接的客户ID、相同 的主题和相同的订阅名再次调用会话上的createDurableSubscriber方法,那么该持久订阅就会被激活。JMS provider会象客户发送客户处于非激活状态时所发布的消息。

持久订阅在某个时刻只能有一个激活的订阅者。持久订阅在创建之后会一直保留,直到应用程序调用会话上的unsubscribe方法。

4.2.7.本地事务

在一个JMS客户端,可以使用本地事务来组合消息的发送和接收。JMS Session接口提供了commit和rollback方法。事务提交意味着生产的所有消息被发送,消费的所有消息被确认;事务回滚意味着生产的所有消 息被销毁,消费的所有消息被恢复并重新提交,除非它们已经过期。

事务性的会话总是牵涉到事务处理中,commit或rollback方法一旦被调用,一个事务就结束了,而另一个事务被开始。关闭事务性会话将回滚其中的事务。

需要注意的是,如果使用请求/回复机制,即发送一个消息,同时希望在同一个事务中等待接收该消息的回复,那么程序将被挂起,因为知道事务提交,发送操作才会真正执行。

需要注意的还有一个,消息的生产和消费不能包含在同一个事务中。

 4.3.JMS 规范的变迁

JMS的最新版本的是1.1。它和同1.0.2版本之间最大的差别是,JMS1.1通过统一的消息传递域简化了消息传递。这不仅简化了JMS API,也有利于开发人员灵活选择消息传递域,同时也有助于程序的重用和维护。

5.Active介绍

 5.1.安装与配置

官方下载地址:http://activemq.apache.org/download.html

最新版本为5.10.0 Release,笔者使用的是测试使用5.8.0

解压apache-activemq-5.9-incubator.zip(或者apache-activemq-5.9-incubator.tar.gz)目录如下:

+bin (windows下面的bat和unix/linux下面的sh)

+conf (activeMQ配置目录,包含最基本的activeMQ配置文件)

+data (默认是空的)

+docs (index,replease版本里面没有文档,不知道为啥不带)

+example (几个例子)

+lib (activemMQ使用到的lib)

-apache-activemq-4.1-incubator.jar (ActiveMQ的binary)

-LICENSE.txt

-NOTICE.txt

-README.txt

-user-guide.html

你可以使用bin\activemq.bat(activemq) 启动,如果一切顺利,你就会看见类似下面的信息:

几个小提示

  1. 这个仅仅是最基础的ActiveMQ的配置,很多地方都没有配置因此不要直接使用这个配置用于生产系统。

  2. 有的时候由于端口被占用,导致ActiveMQ错误,ActiveMQ可能需要以下端口1099(JMX),61616(默认的TransportConnector)

  3.  如果没有物理网卡,或者MSLoopBackAdpaterMulticast会报一个错误。

    ActiveMQ技术文档_第1张图片

    5.2.测试使用

由于ActiveMQ是一个独立的jms provider,所以我们不需要其他任何第三方服务器就可以马上做我们的测试了.编译example目录下面的程序 ProducerTool/ConsumerTool 是JMS参考里面提到的典型应用,Producer产生消息,Consumer消费消息,而且这个例子还可以加入参数帮助你测试刚才启动的本地ActiveMQ或者是远程的ActiveMQ。

运行ProducerAndConsumerTool,可在控制台上观察消息发送与接收的详细信息。

 6.分布式ActiveMQ集群

官方资料:http://activemq.apache.org/clustering.html

  6.1.Queue consumer clusters

同一个queue,如果一个consumer失效,那么未被确认的消息都会被发送到这个queue的其它consumer上。

如果某个consumer处理消息比较快,那么它将处理更多的消息。

(Queueconsumer clusters 不需要特殊的配置。)

  6.2.Master-Slave高可用性

主要目的是实现AMQ的高可用性和容错,如果某broker挂了,需要等待它重启才能继续处理消息。而如果消息被复制到slave上,在当master挂了时,可以直接切换到slave导致消息不会丢失。分为3种形式:

6.2.1.Pure master-slave

该方式已经逐渐被淘汰:http://activemq.apache.org/pure-master-slave.html

6.2.2.Shared File System Master Slave

官方资料:http://activemq.apache.org/masterslave.html

利用共享文件系统:当多台机器上都部署了AMQ时,指定这些机器的一个共享的文件路径作为存储。

存储默认是基于AMQ的kahaDB(底层是文件系统)实现。

当一个AMQ实例获得了共享文件的锁,这个实例就成为了Master,其它实例即为Slave。如果这时Master挂了,其它AMQ实例会竞争共享文件的锁,获得锁的就成为Master,其它实例还是Slave。部署时Slave没有限制数,而且自动切换Master不需要人工干预。(官方资料有详细的过程图片介绍)

6.2.3.JDBC Master Slave

官方资料:http://activemq.apache.org/masterslave.html

其实与Shared File System一样,只是把共享文件系统换成数据库作为存储。方便实用,但要保证数据库的高可用性。

 6.3.Broker Cluster中的静态与动态发现

如何让一个broker知道网络上的其它多个broker呢?主要分为静态发现和动态发现两种类型。

6.3.1.The Static Transport

官网资料:http://activemq.apache.org/static-transport-reference.html

所谓静态发现:就是将所有已知的broker uri连接时手工进行配置,对client端uri地址做相应修改。

关于failover:

当一个client连接到某个broker,而这个broker挂了,客户端就需要自动连接到网络上其它已知的broker上。

AMQ使用failover协议实现该功能,但需要在client连接时将所有broker以硬编码的形式进行配置。

AMQ的failover协议官方资料:http://activemq.apache.org/failover-transport-reference.html

6.3.2.The Discovery Transport

官网资料:http://activemq.apache.org/static-transport-reference.html

所谓动态发现,就是部署前不需要知道所有AMQ实例的uri地址,只要进行相关配置,启动后让AMQ自己检测。

需要修改AMQ配置文件,同时client端连接uri地址也要相应修改。

 6.4.Network of Broker

主要目的是实现负载均衡,提高消息处理能力。

一个client1连接broker1发送消息,另一个client2连接broker2消费消息,这时就需要将broker1上的消息路由到broker2上。而当broker2上的consumer挂了,也需要将消息转发到其它的有consumer的broker上,避免消息大量堆积无法处理,目前的解决方案是Network of Broker。

官方资料:http://activemq.apache.org/networks-of-brokers.html