ActiveMQ是一个开源的消息队列,在企业级应用中应用广泛。新工作中的项目正在使用该产品,因此在周末抽空对MQ做一个初步研究,以便能更快的熟悉项目。
1、ActiveMQ安装与启动
ActiveMQ的下载地址:
http://activemq.apache.org/activemq-5145-release.html
我这里选择的是Windows Distribution.下载完成之后解压,双击:
\apache-activemq-5.14.5\bin\win32\activemq.bat
由于我使用的是32位Windows,因此选择的是win32下的bat文件,64位的同学请选择win64文件夹下的。
显示如下:
ActiveMQ启动成功。试着访问该MQ:
http://localhost:8161/
点击Manage ActiveMQ broker进入管理界面,新建一个队列。进入管理界面时需要账户密码,默认为:admin/admin
点击Queues进入队列管理界面,在Queue Name输入要创建的队列名称:firstMQ,点击Create后队列创建完成。可以看到队列的基本信息:等待消息数、消息订阅者个数、入列消息数、出列消息数等。
2、消息队列访问
在上一步中创建了一个消息队列,该队列的IP地址为localhost(由于是在本机做测试,没有部署在专用服务器上),端口为61616(ActiveMQ默认端口),名称为firstMQ的消息队列。那么现在试着去访问它。所谓的访问,无非就是给队列发消息和从队列收消息两种。
在做这两件事之前,需要做一些基础工作(基于Spring):
引入ActiveMQ工具Jar包。为了省事引入的是ActiveMQ的全量包
<dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.14.4</version> </dependency>
添加ActiveMQ的配置文件,往Spring容器中添加必要对象:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"> <!--ActiveMQ队列的连接工厂,应用从工厂中获取连接去访问队列 --> <amq:connectionFactory id="amqConnectionFactory" brokerURL="tcp://localhost:61616" userName="admin" password="admin"></amq:connectionFactory> <!--定义队列连接工厂的Spring实现,使用定义的ActiveMQ连接工厂初始化 --> <bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"> <constructor-arg ref="amqConnectionFactory"></constructor-arg> </bean> <!--定义要访问的队列,也就是定义的firstMQ --> <bean id="dest" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>firstMQ</value> </constructor-arg> </bean> <!--定义消息服务模板,应用使用该模板发送消息 --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!--消息队列连接工厂 --> <property name="connectionFactory" ref="connectionFactory" /> <!--发送消息的默认目的地,也就是上面定义的消息队列 --> <property name="defaultDestination" ref="dest" /> <property name="receiveTimeout" value="10000" /> </bean> <!--定义一个监听器,监听消息队列中的新消息 --> <bean id="queueMessageConsumer" class="com.mhui.TestListener" /> <!--消息订阅者容器,把消息监听器放入该容器中,可以接收从配置的消息队列监听到的消息 --> <!--DefaultMessageListenerContainer监听到消息后,会调用 messageListener中的onMessage方法 --> <bean id="queueConsumerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="dest" /> <property name="messageListener" ref="queueMessageConsumer" /> </bean> </beans>
配置完成后,让该配置生效,使配置监听器能够监听到该配置。在web.xml中添加:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:../spring-activemq.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
这样,基础配置完成后,下面完成相关的消息监听类和发送类。
消息监听类必须实现MessageListener类,这样才能被监听容器回调:
package com.mhui; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestListener implements MessageListener { Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void onMessage(Message message) { TextMessage tm = (TextMessage) message; try { logger.info("收到一条消息,消息内容为 : " + tm.getText()); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
定义一个消息发送者,消息发送者使用已经定义好的jmsTemplate发送消息:
package com.mhui; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; import org.springframework.stereotype.Service; @Service public class TestMessageSender { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired JmsTemplate jmsTemplate; public void sendOneMessage(String message){ jmsTemplate.send(new MessageCreator() { @Override public Message createMessage(Session session) throws JMSException { logger.info("发送一条文本消息:"); return session.createTextMessage(message); } }); } }
测试代码:
package com.mhui; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @RequestMapping("/login") @Controller public class Test { Logger logger = LoggerFactory.getLogger(Test.class); @Autowired TestMessageSender sender; @RequestMapping(value="/logPage") @ResponseBody public void login(){ sender.sendOneMessage("hello activeMQ..."); } }
运行程序,发送一条消息。可以在队列管理界面上可以看到消息队列的实时情况。可以看到,该队列有1个消息订阅者,入列了两个消息,出列了两个消息,没有消息阻塞。
在控制台,同样可以看到消息的收发情况。
当把消息的接收者屏蔽以后(在spring-active.xml配置文件中将监听者和容器删除),队列中的消息没有接收者,消息会阻塞在队列中:
连发三条消息:
当消息接收者恢复之后,3条阻塞的消息会依次出列:
3、总结
一个简易消息队列完整的流程如下:
- 建立消息队列
- 建立连接工厂
- 建立消息服务模板
- 定义消息发布者和消息监听者
完整地做完这些动作,对消息队列能有一个清晰的认识。实际项目中运用消息队列比这要复杂得多,还需要更多的时间和精力去实践才能掌握好。