我与ActiveMQ的恩怨情仇

Posted 程序员进化之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我与ActiveMQ的恩怨情仇相关的知识,希望对你有一定的参考价值。

我与ActiveMQ的恩怨情仇

概要

为什么写道我与ActiveMQ的恩怨情仇 ,其实这一切缘于最初对ActiveMQ的学习和应用.......

ActiveMQ 介绍

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

ActiveMQ特性

⒈ 多种语言和协议编写客户端。语言: Java,C,C++,C#,Ruby,Perl,Python,php。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP

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

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

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

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

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

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

⒏ 支持Ajax

⒐ 支持与Axis的整合

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

干货开始

ActiveMQ组成

其实ActiveMQ的组成和其他消息队列类似,或者基本相同,都有生产者生产消息,用于存消息的队列(Queue)或者(Topics)。然后还有从队列里取消息的消费者。

基础版的消费者和生产者

生产者

 
   
   
 
  1. package com.hzfh.activiemq.service;


  2. import com.alibaba.fastjson.JSONObject;

  3. import com.hzfh.entity.MessageTest;

  4. import org.apache.activemq.ActiveMQConnectionFactory;

  5. import org.springframework.stereotype.Component;


  6. import javax.jms.*;


  7. /**

  8. * com.hzfh.activiemq.service

  9. *

  10. * @author rencc

  11. * @Note 生产者发送消息 TextMessage

  12. * @Date 2017-08-22 17:23

  13. */

  14. @Component

  15. public class ProducerModel {

  16. private static final String url="tcp://127.0.0.1:61616";

  17. private static final String queueName="2017-08-23";

  18. public void sendMessage(MessageTest messageTest) throws JMSException {

  19. //1,创建ConnectionFacytory

  20. ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);


  21. //2,创建连接Connection

  22. Connection connection = connectionFactory.createConnection();


  23. //3,启动链接

  24. connection.start();


  25. //4创建会话

  26. Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);


  27. //5,创建一个目标

  28. Destination destination = session.createQueue(queueName);


  29. //6,创建一个生产者

  30. MessageProducer producer = session.createProducer(destination);


  31. //7,创建消息

  32. TextMessage textMessage = session.createTextMessage(JSONObject.toJSONString(messageTest));

  33. textMessage.setIntProperty("id",messageTest.getId());//带"过滤"的消息选择器

  34. //8发布消息

  35. producer.send(textMessage);

  36. //9,关闭连接

  37. connection.close();

  38. }

  39. }

消费者

 
   
   
 
  1. package com.hzfh.activiemq.service;


  2. import com.alibaba.fastjson.JSONObject;

  3. import com.hzfh.entity.MessageTest;

  4. import org.apache.activemq.ActiveMQConnectionFactory;

  5. import org.springframework.stereotype.Component;


  6. import javax.jms.*;


  7. /**

  8. * com.hzfh.activiemq.service

  9. *

  10. * @author rencc

  11. * @Note 消费者消费发送消息 TextMessage

  12. * @Date 2017-08-22 17:29

  13. */

  14. @Component

  15. public class ConsumerModel {

  16. private static final String url="tcp://127.0.0.1:61616";

  17. private static final String queueName="2017-08-22";

  18. public MessageTest createCustomer(int id) throws JMSException, InterruptedException {

  19. MessageTest messageTest = new MessageTest();

  20. //1,创建ConnectionFacytory

  21. ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);


  22. //2,创建连接Connection

  23. Connection connection = connectionFactory.createConnection();


  24. //3,启动链接

  25. connection.start();


  26. //4创建会话

  27. Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);


  28. //5,创建一个目标

  29. Destination destination = session.createQueue(queueName);


  30. //6 创建一个消费者,消息过滤选择消费

  31. MessageConsumer consumer = session.createConsumer(destination,"id ="+id);


  32. //7创建一个监听器

  33. consumer.setMessageListener(new MessageListener() {

  34. public void onMessage(Message message) {

  35. TextMessage textMessage = (TextMessage) message;

  36. try {

  37. JSONObject parse = JSONObject.parseObject(textMessage.getText());

  38. messageTest = JSONObject.toJavaObject(parse, MessageTest.class);

  39. } catch (JMSException e) {

  40. e.printStackTrace();

  41. }

  42. }

  43. });

  44. //8,关闭连接

  45. connection.close();

  46. return messageTest;

  47. }

  48. }

升级版生产者和消费者

所谓升级版,其实是ActiveMQ集成Spring Boot后,采用注解配置后监听器的方式来实现,生产者生产消息,消费者监听消费的队列。(实时消费)

Spring Boot 注解配置(application.properties)

 
   
   
 
  1. spring.activemq.broker-url=tcp://localhost:61616

生产消息类

 
   
   
 
  1. package com.hzfh.activiemq;


  2. import com.alibaba.fastjson.JSONObject;

  3. import com.hzfh.entity.MessageTest;

  4. import org.apache.activemq.command.ActiveMQQueue;

  5. import org.springframework.beans.factory.annotation.Autowired;

  6. import org.springframework.jms.core.JmsMessagingTemplate;

  7. import org.springframework.jms.core.MessageCreator;

  8. import org.springframework.stereotype.Service;


  9. import javax.jms.*;


  10. /**

  11. * com.hzfh.activiemq

  12. *

  13. * @author rencc

  14. * @Note 生产消息的类

  15. * @Date 2017-08-22 11:18

  16. */

  17. public class CreateMessage implements MessageCreator{

  18. @Override

  19. public Message createMessage(Session session) throws JMSException {

  20. MessageTest message = new MessageTest();

  21. message.setId(110);

  22. message.setMessage("测试Object");

  23. String json = JSONObject.toJSONString(message);

  24. return session.createTextMessage(json);

  25. }

  26. }

生产发送消息实现

仅仅是为了实现效果,我在Spring Boot的启动类里直接使用发消息。

 
   
   
 
  1. package com.hzfh;


  2. import com.hzfh.activiemq.CreateMessage;

  3. import org.springframework.beans.factory.annotation.Autowired;

  4. import org.springframework.boot.CommandLineRunner;

  5. import org.springframework.boot.SpringApplication;

  6. import org.springframework.boot.autoconfigure.SpringBootApplication;

  7. import org.springframework.jms.core.JmsTemplate;


  8. @SpringBootApplication

  9. public class WebsocketApplication implements CommandLineRunner {

  10. @Autowired

  11. JmsTemplate jmsTemplate;

  12. public static void main(String[] args) {

  13. SpringApplication.run(WebsocketApplication.class, args);

  14. }


  15. @Override

  16. public void run(String... strings) throws Exception {

  17. jmsTemplate.send("test",new CreateMessage());//test 为队列名

  18. }

  19. }

消息监听器

 
   
   
 
  1. package com.hzfh.activiemq;


  2. import com.alibaba.fastjson.JSONObject;

  3. import com.hzfh.entity.MessageTest;

  4. import org.springframework.jms.annotation.JmsListener;

  5. import org.springframework.stereotype.Component;


  6. /**

  7. * com.hzfh.activiemq

  8. *

  9. * @author rencc

  10. * @Note 消息队列监听器

  11. * @Date 2017-08-22 11:26

  12. */

  13. @Component

  14. public class Receiver {

  15. private static final String destination="test";//要监听的队列名


  16. @JmsListener(destination = destination)

  17. public void receiveMessage(String message){

  18. JSONObject parse = JSONObject.parseObject(message);

  19. MessageTest messageTest = JSONObject.toJavaObject(parse, MessageTest.class);

  20. System.out.println("接收到:"+messageTest.toString());

  21. }

  22. }

恩怨情仇

以上内容都是干货,自此之后开始唠叨,我与ActiveMQ的恩怨情仇。
  研究ActiveMQ是因为,公司开发的基于Spring Boot架构的企业后台管理系统,在工作流的待审待办消息那儿,没有实现实时的消息提醒,以至于用户每次查看新消息都需要F5,用户体验性差。所以前几天接到任务,实现公司现有系统的待审待办消息实时提醒功能。
  百度相关内容之后,发现实现方式要么Ajax轮询或者借助消息中间件。至此决定采用ActiveMQ消息队列。初步研究后,使用SpringBoot搭建一个Demo实现了后台生产消息,监听器消费消息。但是我需要的是要实现浏览器实时的更新提醒消息。所以深入研究之后,从官网上找到了ajax方式监听消息队列的方式。可就在此时,难题来了,ajax方式需要配置servlet来后台处理,官网所有文档指出的都是在web.xml里配置servlet。

 
   
   
 
  1. <!-- 配置支持ajax的jms -->

  2. <context-param>

  3. <param-name>org.apache.activemq.brokerURL</param-name>

  4. <param-value>tcp://localhost:61616</param-value>

  5. <description>连接到消息中间件的URL</description>

  6. </context-param>

  7. <servlet>

  8. <servlet-name>AjaxServlet</servlet-name>

  9. <servlet-class>org.apache.activemq.web.AjaxServlet</servlet-class>

  10. <load-on-startup>1</load-on-startup>

  11. </servlet>

  12. <servlet-mapping>

  13. <servlet-name>AjaxServlet</servlet-name>

  14. <url-pattern>/amq/*</url-pattern>

  15. </servlet-mapping>

  但是SpringBoot是没有web.xml的,后来就去研究SpringBoot注册Servlet的方式,使用注解也好,还是在配置类里注册也好,都是不行。哎,头疼。

注册方式

 
   
   
 
  1. package org.springboot.sample;


  2. import org.springboot.sample.servlet.MyServlet;

  3. import org.springframework.boot.SpringApplication;

  4. import org.springframework.boot.autoconfigure.SpringBootApplication;

  5. import org.springframework.boot.context.embedded.ServletRegistrationBean;

  6. import org.springframework.boot.web.servlet.ServletComponentScan;

  7. import org.springframework.context.annotation.Bean;

  8. import org.springframework.web.servlet.DispatcherServlet;


  9. @SpringBootApplication

  10. public class SpringBootSampleApplication {


  11. /**

  12. * 使用代码注册Servlet

  13. *

  14. * @return

  15. * @author SHANHY

  16. * @create 2016年1月6日

  17. */

  18. @Bean

  19. public ServletRegistrationBean servletRegistrationBean() {

  20. return new ServletRegistrationBean(new MyServlet(), "/amp/*");

  21. }


  22. public static void main(String[] args) {

  23. SpringApplication.run(SpringBootSampleApplication.class, args);

  24. }

  25. }

但是还需要再MyServlet类前添加

@WebServlet(urlPatterns="/xs/*", description="Servlet的说明")

   孰不知我要配置的是官方jar包里的类,这怎么加注解....后来打算,大不了重写官方给的AjaxServlet.不管怎么样,重写肯定OK的。
   记得决定这样做当时已经是下班了,回家的路上,我在10号线地铁上反复思考,假如这种方式可以,那如何保证队列里的消息和数据库待审待办表里的数据永远保持一致呢?队列里添加消息容易,但是删除呢?思考良久发现不可行。至此,我两天的研究的时间白费了。前期考察不充分,血淋林赤裸裸的教训。

   第三天痛定思痛,采用ajax轮询实现。

写在最后

ActiveMQ应用于异步消息同步,多用于系统之间消息同步,减缓用户延迟等场景。


以上是关于我与ActiveMQ的恩怨情仇的主要内容,如果未能解决你的问题,请参考以下文章

红帽 与 CentOS 之间的恩怨情仇

CRM江湖:CRM厂商之间的恩怨情仇

带你完全理解Python中的metaclass,type,class之间的恩怨情仇...

AJAXAxiso和Fetch的那些恩怨情仇

文本挖掘,带你看金庸笔下不一样的恩怨情仇

Autolayout与ScrollView的恩怨情仇