ActiveMQ
1.关于ActiveMQ
ActiveMQ是Apache 出品的一款开源消息中间件,它实现了JMS标准。
2.关于JMS
JMS API是一个消息服务规范,用于应用程序之间的通信。
3.关于消息模型
- 3.1 点对点(Point-To-Point)
使用队列(Queue)作为消息通信载体,即生产者与消费者模式。一条消息只能被一个消费者使用,未被消费的消息在队列中保留直到被消费或超时
- 3.2 发布/订阅(Publisher-Subscriber)
使用主题(Topic)作为消息通信载体,类似于广播模式。发布者发布一条消息,该消息通过主题传递给所有在线的订阅者。普通订阅者在发布消息之后上线或者是发布消息之后订阅,将会错过这条消息;持久订阅者不会出现这个问题。
4.消息确认方式
- 4.1 自动确认模式,不需客户端进行确认
Session.AUTO_ACKNOWLEDGE 1
- 4.2 客户端进行确认
Session.CLIENT_ACKNOWLEDGE 2
如果创建Session时,acknowledgeMode = Session.CLIENT_ACKNOWLEDGE;那么后续的代码需要message.acknowledge()才能确认消息。 - 4.3 允许副本的确认模式
Session.DUPS_OK_ACKNOWLEDGE 3
- 4.4 支持实务,需要提交实务
Session.SESSION_TRANSACTED 0
创建Session,需要设置transacted = true,acknowledgeMode=Session.SESSION_TRANSACTED;那么后续代码需要session.commit()才能提交。
5.控制台的ActiveMQ
- 下载ActiveMQ(activemq),选择Windows
- 解压
- 运行:32位在[ActiveMQ_install_dir]/bin/Win32/,双击activemq.bat;64位在[ActiveMQ_install_dir]/bin/Win64/,双击activemq.bat
- 5.1 引入依赖
<dependency> <groupId>javax.jms</groupId> <artifactId>javax.jms-api</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.15.9</version> </dependency>
- 5.2 编写常量类 Constant.java
import org.apache.activemq.ActiveMQConnection; public interface Constant { //用户名 String USERNAME = ActiveMQConnection.DEFAULT_USER; //密码 String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD; //ActiveMQ地址 String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL; Integer MESSAGE_COUNT = 10; }
- 5.3 点对点模型
- 5.3.1 生产者 PointToPointProducer.java
import cn.techpan.happiness.mq.active_mq.console.Constant; import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import java.util.Objects; /** * ActiveMQ P2P 生产端 */ public class PointToPointProducer { public static void main(String[] args) { Connection connection = null; try { //1获取ActiveMQ的连接工厂 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL); //2使用连接工厂创建连接 connection = connectionFactory.createConnection(); //3开启连接 connection.start(); //4创建session对象 // 参数一:是否支持事务 // 参数二:消息确认方式 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5创建队列 Queue queue = session.createQueue("queue"); //6创建消息生产者(MessageProducer) MessageProducer messageProducer = session.createProducer(queue); //7发送消息 sendMessage(session, messageProducer); } catch (JMSException e) { e.printStackTrace(); } finally { try { Objects.requireNonNull(connection).close(); } catch (JMSException e) { e.printStackTrace(); } } } private static void sendMessage(Session session, MessageProducer messageProducer) { for (int i = 0; i < Constant.MESSAGE_COUNT; i++) { try { TextMessage textMessage = session.createTextMessage(); textMessage.setText("PointToPointProducer 发送消息: " + i); System.out.println("PointToPointProducer 发送消息: " + i); messageProducer.send(textMessage); } catch (JMSException e) { e.printStackTrace(); } } } }
- 5.3.2 消费者 PointToPointCustomer.java
import cn.techpan.happiness.mq.active_mq.console.Constant; import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.MessageConsumer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import java.util.Objects; /** * ActiveMQ P2P 消费端 */ public class PointToPointCustomer { public static void main(String[] args) { Connection connection = null; try { ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL); connection = connectionFactory.createConnection(); connection.start(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = session.createQueue("queue"); //创建消息消费者(MessageConsumer) MessageConsumer messageConsumer = session.createConsumer(queue); while (true) {//同步接收消息(异步可使用MessageListener) //同步接收:主线程阻塞式等待下一个消息的到来,可以设置timeout,超时则返回null。 //同步接收又称为阻塞式接收 //同步接收,是在获取MessageConsumer实例之后,调用以下的API: //receive():Message // 获取下一个消息。这个调用将导致无限期的阻塞,直到有新的消息产生。 //receive(long timeout):Message // 获取下一个消息。这个调用可能导致一段时间的阻塞,直到超时或者有新的消息产生。超时则返回null。 //receiveNoWait():Message // 获取下一个消息。这个调用不会导致阻塞,如果没有下一个消息,直接返回null。 TextMessage textMessage = (TextMessage) messageConsumer.receive(10000); if (textMessage != null) { System.out.println("PointToPointCustomer 接收消息: " + textMessage.getText()); } else { break; } } } catch (JMSException e) { e.printStackTrace(); } finally { try { Objects.requireNonNull(connection).close(); } catch (JMSException e) { e.printStackTrace(); } } } }
- 5.4 发布/订阅模型
- 5.4.1 生产者 PublishSubscribeProducer.java
import cn.techpan.happiness.mq.active_mq.console.Constant; import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import java.util.Objects; /** * ActiveMQ Pub-Sub 生产者 */ public class PublishSubscribeProducer { public static void main(String[] args) { Connection connection = null; try { ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL); connection = connectionFactory.createConnection(); connection.start(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //创建主题 Topic topic = session.createTopic("topic"); //创建生产者 MessageProducer messageProducer = session.createProducer(topic); //发送消息 sendMessage(session, messageProducer); } catch (JMSException e) { e.printStackTrace(); } finally { try { Objects.requireNonNull(connection).close(); } catch (JMSException e) { e.printStackTrace(); } } } private static void sendMessage(Session session, MessageProducer messageProducer) throws JMSException { for (int i = 0; i < Constant.MESSAGE_COUNT; i++) { TextMessage textMessage = session.createTextMessage(); String message = "PublishSubscribeProducer Send Message:" + i; System.out.println(message); textMessage.setText(message); messageProducer.send(textMessage); } } }
- 5.4.2 消费者 PublishSubscribeCustomer.java
import cn.techpan.happiness.mq.active_mq.console.Constant; import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import java.util.Objects; /** * ActiveMQ Pub-Sub 消费者 */ public class PublishSubscribeCustomer { public static void main(String[] args) { Connection connection = null; try { ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL); connection = connectionFactory.createConnection(); connection.start(); //创建session,不支持事务,自动确认 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //创建topic Topic topic = session.createTopic("topic"); //创建普通订阅者 MessageConsumer messageConsumer = session.createConsumer(topic); while (true) { //同步获取消息,等待超时时间为10000ms。 TextMessage textMessage = (TextMessage) messageConsumer.receive(10000); if (textMessage != null) { System.out.println("Message Arrived: " + textMessage.getText()); } else { break; } } } catch (JMSException e) { e.printStackTrace(); } finally { try { Objects.requireNonNull(connection).close(); } catch (JMSException e) { e.printStackTrace(); } } } }
6.与Spring集成
- 6.1 引入依赖
<dependency> <groupId>javax.jms</groupId> <artifactId>javax.jms-api</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.15.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <scope>provided</scope> </dependency>
- 6.2 消息监听器
- 6.2.1 QueueMessageListener.java
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * 配置消息监听器 */ @Slf4j @Component public class QueueMessageListener implements MessageListener { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { if (textMessage != null) { log.info("message come from: {}, content: {}", message.getJMSDestination(), textMessage.getText()); } } catch (JMSException e) { e.printStackTrace(); } } }
- 6.2.2 TopicMessageListener.java
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * 配置监听器 */ @Slf4j @Component public class TopicMessageListener implements MessageListener { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { if (textMessage != null) { log.info("topic01: message come from: {}, content: {}", message.getJMSDestination(), textMessage.getText()); } } catch (JMSException e) { e.printStackTrace(); } } }
- 6.2.1 QueueMessageListener.java
- 6.3 消息生产者
- 6.3.1 QueueProducer.java
import lombok.extern.slf4j.Slf4j; import org.springframework.jms.core.JmsTemplate; import org.springframework.stereotype.Component; import javax.jms.Destination; /** * 消息发送 */ @Slf4j @Component public class QueueProducer { private JmsTemplate queueJmsTemplate; public QueueProducer(JmsTemplate queueJmsTemplate) { this.queueJmsTemplate = queueJmsTemplate; } public void sendMessageToDefaultDestination(String message) { Destination destination = queueJmsTemplate.getDefaultDestination(); log.info("a message has send to {}", destination); queueJmsTemplate.send(session -> session.createTextMessage(message)); } public void sendMessage(Destination destination, String message) { log.info("a message has send to {}", destination); queueJmsTemplate.send(destination, session -> session.createTextMessage(message)); } }
- 6.3.2 TopicProducer.java
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jms.core.JmsTemplate; import org.springframework.stereotype.Component; import javax.jms.Destination; /** * 消息发送 */ @Slf4j @Component public class TopicProducer { private JmsTemplate jmsTemplate; public TopicProducer(@Qualifier("topicJmsTemplate") JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public void sendMessageToDefaultDestination(String message) { Destination destination = jmsTemplate.getDefaultDestination(); log.info("a message has publish to {}", destination); jmsTemplate.send(session -> session.createTextMessage(message)); } }
- 6.3.1 QueueProducer.java
- 6.4 配置文件 SpringConfig.Java
import lombok.extern.slf4j.Slf4j; import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQTopic; import org.apache.activemq.spring.ActiveMQConnectionFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jms.connection.CachingConnectionFactory; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.listener.DefaultMessageListenerContainer; import javax.jms.Destination; import javax.jms.MessageListener; @Slf4j @Configuration @ComponentScan(value = "cn.techpan.happiness.mq.active_mq.spring.*") public class SpringConfig { /** * 创建ActiveMQ连接工厂,实现客户端与ActiveMQ服务提供者连接 * @return ActiveMQConnectionFactory */ @Bean public ActiveMQConnectionFactory getActiveMQConnectionFactory() { ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(); activeMQConnectionFactory.setUserName(ActiveMQConnectionFactory.DEFAULT_USER); activeMQConnectionFactory.setPassword(ActiveMQConnectionFactory.DEFAULT_PASSWORD); activeMQConnectionFactory.setBrokerURL(ActiveMQConnectionFactory.DEFAULT_BROKER_URL); return activeMQConnectionFactory; } /** * 创建Spring Caching连接工厂,并注入ActiveMQ连接工厂,用于对ActiveMQ连接工厂的管理 * @param activeMQConnectionFactory ActiveMQ连接工厂 * @return CachingConnectionFactory */ @Bean public CachingConnectionFactory getCachingConnectionFactory(ActiveMQConnectionFactory activeMQConnectionFactory) { CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(); cachingConnectionFactory.setTargetConnectionFactory(activeMQConnectionFactory); //设置缓存大小 cachingConnectionFactory.setSessionCacheSize(100); return cachingConnectionFactory; } /** * 创建Destination * @return Queue */ @Bean public ActiveMQQueue activeMQQueue() { return new ActiveMQQueue("spring-queue"); } /** * 创建JmsTemplate(queue) * @param cachingConnectionFactory 连接工厂 * @param activeMQQueue Destination * @return JmsTemplate */ @Bean("queueJmsTemplate") public JmsTemplate getQueueJmsTemplate(CachingConnectionFactory cachingConnectionFactory, Destination activeMQQueue) { JmsTemplate queueJmsTemplate = new JmsTemplate(cachingConnectionFactory); //MQ 模型。 true - pub/sub false - p2p queueJmsTemplate.setPubSubDomain(false); queueJmsTemplate.setDefaultDestination(activeMQQueue); return queueJmsTemplate; } /** * 消息监听容器 负责处理消息接收的注册、事务管理、资源获取与释放和异常转换等 * @param cachingConnectionFactory 连接工厂 * @param activeMQQueue Destination * @param queueMessageListener 消息监听器 * @return DefaultMessageListenerContainer */ @Bean public DefaultMessageListenerContainer queueMessageListenerContainer(CachingConnectionFactory cachingConnectionFactory, Destination activeMQQueue, MessageListener queueMessageListener) { DefaultMessageListenerContainer queueMessageListenerContainer = new DefaultMessageListenerContainer(); //注入ConnectionFactory queueMessageListenerContainer.setConnectionFactory(cachingConnectionFactory); //设置Destination queueMessageListenerContainer.setDestination(activeMQQueue); //注册MessageListener queueMessageListenerContainer.setMessageListener(queueMessageListener); return queueMessageListenerContainer; } /** * 创建Destination * @return ActiveMQTopic */ @Bean public ActiveMQTopic activeMQTopic() { return new ActiveMQTopic("spring-topic"); } /** * 创建JmsTemplate(Topic) * @param cachingConnectionFactory 连接工厂 * @param activeMQTopic Destination * @return JmsTemplate */ @Bean("topicJmsTemplate") public JmsTemplate getTopicJmsTemplate(CachingConnectionFactory cachingConnectionFactory, Destination activeMQTopic) { JmsTemplate topicJmsTemplate = new JmsTemplate(cachingConnectionFactory); topicJmsTemplate.setPubSubDomain(true); topicJmsTemplate.setDefaultDestination(activeMQTopic); return topicJmsTemplate; } /** * 消息监听容器 * @param cachingConnectionFactory 连接工厂 * @param activeMQTopic Destination * @param topicMessageListener 消息监听器 * @return DefaultMessageListenerContainer */ @Bean public DefaultMessageListenerContainer topicMessageListenerContainer(CachingConnectionFactory cachingConnectionFactory, Destination activeMQTopic, MessageListener topicMessageListener) { DefaultMessageListenerContainer topicMessageListenerContainer = new DefaultMessageListenerContainer(); topicMessageListenerContainer.setConnectionFactory(cachingConnectionFactory); topicMessageListenerContainer.setDestination(activeMQTopic); topicMessageListenerContainer.setMessageListener(topicMessageListener); return topicMessageListenerContainer; } }
- 6.5 消息发送服务
- 6.5.1 QueueMessageService.java
import cn.techpan.happiness.mq.active_mq.spring.jms.producer.QueueProducer; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Slf4j @Service public class QueueMessageService { //注入消息生产者 private QueueProducer queueProducer; public QueueMessageService(QueueProducer queueProducer) { this.queueProducer = queueProducer; } public void sendMessage() { log.info("ready for sending message"); log.info("producer is sending messages!"); for (int i = 0; i < 10; i++) { String message = "message NO." + i; queueProducer.sendMessageToDefaultDestination(message); } log.info("message send over"); } }
- 6.5.2 TopicMessageService.java
import cn.techpan.happiness.mq.active_mq.spring.jms.producer.TopicProducer; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Slf4j @Service public class TopicMessageService { //注入消息生产者 private TopicProducer topicProducer; public TopicMessageService(TopicProducer topicProducer) { this.topicProducer = topicProducer; } public void sendMessage() { log.info("ready for publishing message"); log.info("producer is publishing messages!"); for (int i = 0; i < 10; i++) { String message = "message NO." + i; topicProducer.sendMessageToDefaultDestination(message); } log.info("message publish over"); } }
- 6.5.1 QueueMessageService.java
- 6.6 主运行类 SpringActiveMQApplication
import cn.techpan.happiness.mq.active_mq.spring.config.SpringConfig; import cn.techpan.happiness.mq.active_mq.spring.message.QueueMessageService; import cn.techpan.happiness.mq.active_mq.spring.message.TopicMessageService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class SpringActiveMQApplication { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class); QueueMessageService queueMessageService = applicationContext.getBean(QueueMessageService.class); queueMessageService.sendMessage(); TopicMessageService topicMessageService = applicationContext.getBean(TopicMessageService.class); topicMessageService.sendMessage(); } }
- 7 参考文档
- 7.1 MQ系列01:JMS API