消息中间件之ActiveMQ
什么是消息中间件MQ
定义
是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的继承。通过提供消息传递和消息排队模型在分布式环境下提供应用解耦,弹性压缩,冗余存储,流量削峰,异步通信,数据同步等功能。
流程
- 发送者把消息发送给消息服务器,消息服务器将消息存放在若干队列/主题中,在合适的时候,消息服务器会将消息转发给接受者。
- 在这个过程中,发送和接受是异步的,也就是发送无需等待,而且发送者和接受者的生命周期也没有必然关系;
- 尤其在发布pub/订阅sub模式下,也可以完成一对多的通信,即让一个消息有多个接受者
特点:
- 采用异步处理模式
- 应用系统之间解耦合
- 发送者和接受者不必了解对方,只需要确认消息
- 发送者和接受者不必同时在线
作用
- 解耦
- 削峰
- 异步
下载与安装
下载地址
http://activemq.apache.org/components/classic/download/
安装(Linux为例)
- 1.将下载文件复制到Linux服务器里
- 2.解压缩 : tar -zxvf apache-activemq-5.15.10-bin.tar.gz
- 3.创建文件夹myactiveMQ : mkdir myactiveMQ
- 4.将解压缩后的文件拷贝到创建文件夹:cp -r apache-activemq-5.15.10 /home/web/myactiveMQ/
- 5.普通启动:
- 进入bin目录:cd apache-activemq-5.15.10/bin/
- 启动: ./activemq start
- 6.检查是否成功启动:ps -ef|grep activemq
- activemq的默认进程端口是61616
- netstat -anp|grep 61616
- 7.停止启动 : ./activemq stop
- 8.带运行日志的启动方式:./activemq start > /myactiveMQ/run_activemq.log
前台访问
- 地址:http://ip地址:8161/admin
- linux中获得ip地址:ifconfig
- 关闭防火墙:service iptables stop
- 用户名/密码:admin/admin
采用61616端口提供JMS服务
采用8161端口提供管理控制台服务
有那些MQ
- Kaflka:大数据专用消息中间件
- RabbitMQ:
- RocketMQ:
- ActiveMQ:
- 其他:
为什么要使用消息中间件
系统之间直接调用实际工程存在的问题
微服务架构后,链式调用是我们写程序时候的一般流程,为了完成一个整体功能会将其拆分成多个函数(或子模块),比如模块A调用模块B,模块B调用模块C,模块C调用模块D。但是大型分布式应用中欧,系统间的RPC交互繁杂,一个功能背后要调用上百个接口并非不可能,从单机架构过渡到分布式微服务的通例
存在的问题
- 系统之间接口耦合比较严重
- 面对大流量并发时,容易被冲垮
- 等待同步存在性能问题
解决问题
- 要做到系统解耦,当新的模块接进来时,可以做到代码改动最小:能够解耦
- 设置流量缓冲池,可以让后端系统按照自身吞吐能力进行消费,不被冲垮:能够削峰
- 强弱依赖梳理能力将非关键调用链路的操作异步化并提升整体系统的吞吐能力:能够异步
点对点消息传递域入门案例
创建maven工程
pom.xml文件
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- acivemq所需要的jar包 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.10</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.16</version>
</dependency>
<!-- 日志等通用配置 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
在点对点的消息传递域中,目的地被称为队列(queue)
-
消息生产者编码:
import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; public class JmsProduce { public static final String ACTIVEMQ_URL="tcp://192.168.159.129:61616"; public static final String QUEUE_NAME="queue01"; public static void main(String[] args) throws JMSException { //1.创建连接工厂,按照指定的URL地址采用默认用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2.通过连接工厂,获得connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); //3.创建会话session //2个参数,第一个事务,第二个签收 Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); //4.创建目的地(具体是队列还是主题) Queue queue = session.createQueue(QUEUE_NAME); //Collection collection = new ArrayList(); //5.创建消息的生产者 MessageProducer messageProducer = session.createProducer(queue); //6.通过使用messageProducer生产3条消息发送到MQ的队列里面 for (int i=0;i<3;i++){ //7.创建消息 TextMessage textMessage = session.createTextMessage("msg==>" + i);//理解为一个字符串 //8.通过messageProducer发送给mq messageProducer.send(textMessage); } //9.关闭资源 messageProducer.close(); session.close(); connection.close(); System.out.println("消息发布到MQ完成"); } }
-
消息消费者编码
import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; import javax.xml.soap.Text; public class JmsConsumer { public static final String ACTIVEMQ_URL="tcp://192.168.159.129:61616"; public static final String QUEUE_NAME="queue01"; public static void main(String[] args) throws JMSException { //1.创建连接工厂,按照指定的URL地址采用默认用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2.通过连接工厂,获得connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); //3.创建会话session //2个参数,第一个事务,第二个签收 Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); //4.创建目的地(具体是队列还是主题) Queue queue = session.createQueue(QUEUE_NAME); //Collection collection = new ArrayList(); //5.创建消息的消费者 MessageConsumer messageConsumer = session.createConsumer(queue); while (true){ //6.接受消息 TextMessage textMessage = (TextMessage) messageConsumer.receive(); if (null != textMessage){ //7.获得消息的内容并处理 System.out.println("消费者接收到消息:"+textMessage.getText()); }else { break; } } //8.释放资源 messageConsumer.close(); session.close(); connection.close(); } }
-
通过监听的方式来消费消息
import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; import java.io.IOException; public class JmsConsumer { public static final String ACTIVEMQ_URL="tcp://192.168.159.129:61616"; public static final String QUEUE_NAME="queue01"; public static void main(String[] args) throws JMSException, IOException { //1.创建连接工厂,按照指定的URL地址采用默认用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2.通过连接工厂,获得connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); //3.创建会话session //2个参数,第一个事务,第二个签收 Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); //4.创建目的地(具体是队列还是主题) Queue queue = session.createQueue(QUEUE_NAME); //Collection collection = new ArrayList(); //5.创建消息的消费者 MessageConsumer messageConsumer = session.createConsumer(queue); /** * 同步阻塞方式(receive()) * 订阅者或接受者调用MessageConsumer的receive()方法来接受消息,receive方法在能够接受到消息之前(或超时之前)将一直阻塞。 * while (true){ //6.接受消息(recive():可以添加过期时间) TextMessage textMessage = (TextMessage) messageConsumer.receive(); if (null != textMessage){ //7.获得消息的内容并处理 System.out.println("消费者接收到消息:"+textMessage.getText()); }else { break; } } //8.释放资源 messageConsumer.close(); session.close(); connection.close(); */ //通过监听的方式来获取消息 //异步非阻塞方式(监听器onMessage()) //订阅者或接受者通过MessageConsumer的setMessageListener(MessageListener listener)注册一个消息监听器 //当消息到达后,系统自动调用监听器MessageListener的onMessage(Message message)方法。 messageConsumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { if (null != message && message instanceof TextMessage) { TextMessage textMessage = (TextMessage) message; try { System.out.println("消费者接收到消息:"+textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } } }); System.in.read();//保证控制台不关闭 //8.释放资源 messageConsumer.close(); session.close(); connection.close(); } }
问题:
1.先生产,只启动1号消费者。问题:1号消费者还能消费消息吗?
能
2.先生产,先启动1号消费者,再启动2号消费者,问题:2号消费者还能消费消息吗吗?
1号可以消费
2号不可以消费3.先启动2个消费者,再生产6条消息,请问,消费情况如何?
3.1 2个消费者都有6条 X
3.2 先到先得,6条全给一个 X
3.3 一人一半 平均分配
特点
- 每个消息只能有一个消费者,类似1对1的关系
- 消息的生产者和消费者之间没有时间上的相关性。无论消费者在生成者发送消息的时候是否处于运行状态,消费者都可以提取消息
- 消息被消费后队列不会再存储,所以消费者不会消费已经被消费掉的消息
小结
JMS开发的基本步骤
- 1.创建一个connection factory
- 2.通过connection factory来创建JMS connection
- 3.启动JMS connection
- 4.通过connection创建JMS session
- 5.创建JMS destination
- 6.创建JMS producer或者创建JMS message并设置destination
- 7.创建JMS consumer或者注册一个JMS message listener
- 8.发送或者接受JMS message(s)
- 9.关闭所有的JMS资源(connection,session,producer,consumer等)
发布订阅消息传递域的入门案例
在发布订阅消息传递域中,目的地被称为主题(topic)
代码
-
消费者
import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; import java.io.IOException; public class JmsConsumer_Topic { public static final String ACTIVEMQ_URL="tcp://192.168.159.129:61616"; public static final String TOPIC_NAME="topic"; public static void main(String[] args) throws JMSException, IOException { //1.创建连接工厂,按照指定的URL地址采用默认用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2.通过连接工厂,获得connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); //3.创建会话session //2个参数,第一个事务,第二个签收 Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); //4.创建目的地(具体是队列还是主题) Topic topic = session.createTopic(TOPIC_NAME); //Collection collection = new ArrayList(); //5.创建消息的消费者 MessageConsumer messageConsumer = session.createConsumer(topic); //通过监听的方式来获取消息 messageConsumer.setMessageListener((message) ->{ if (null != message && message instanceof TextMessage) { TextMessage textMessage = (TextMessage) message; try { System.out.println("消费者接收到Topic消息:"+textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }); System.in.read();//保证控制台不灭 //8.释放资源 messageConsumer.close(); session.close(); connection.close(); } }
-
生产者
import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; public class JmsProduce_Topic { public static final String ACTIVEMQ_URL="tcp://192.168.159.129:61616"; public static final String TOPIC_NAME="topic"; public static void main(String[] args) throws JMSException { //1.创建连接工厂,按照指定的URL地址采用默认用户名和密码 ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL); //2.通过连接工厂,获得connection并启动访问 Connection connection = activeMQConnectionFactory.createConnection(); connection.start(); //3.创建会话session //2个参数,第一个事务,第二个签收 Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); //4.创建目的地(具体是队列还是主题) Topic topic = session.createTopic(TOPIC_NAME); //Collection collection = new ArrayList(); //5.创建消息的生产者 MessageProducer messageProducer = session.createProducer(topic); //6.通过使用messageProducer生产3条消息发送到MQ的队列里面 for (int i=0;i<3;i++){ //7.创建消息 TextMessage textMessage = session.createTextMessage("TOPIC_NAME==>" + i);//理解为一个字符串 //8.通过messageProducer发送给mq messageProducer.send(textMessage); } //9.关闭资源 messageProducer.close(); session.close(); connection.close(); System.out.println("消息发布到MQ完成"); } }
特点
- 生产者将消息发布到topic中,每个消息可以有多个消费者,属于1:N的关系
- 生产者和消费者之间有时间上的相关性。订阅某一个主题的消费者只能消费自它订阅之后发布的消息
- 生产者生产时,topic不保存消息它是无状态的不落地,假如无人订阅就去生产,那就是一条费消息。所以一般是先启动消费者在启动生产者
topic与queue 的区别
JMS
什么是Java消息服务(Java Message Service)
java消息服务指的是两个应用程序之间进行异步通信的API,它为标准信息协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持java应用程序开发。在JavaEE中,当两个应用程序使用JMS进行通信时它们之间并不是直接相连的,而是通过一个共同的消息收发服务组件关联起来以达到解耦/异步削峰的效果
JMS组成结构和特点
- JMS provider:实现JMS接口和规范的消息中间件,也就是MQ服务
- JMS producer:消息生产者,创建和发送JMS消息的客户端应用
- JMS consumer:消息消费者,接收和处理JMS消息的客户端应用
- JMS message:
- 消息头:
- JMSDestination:消息发送的目的地,队列(queue)或者主题(topic)
- JMSDeliveryMode:设置消息的模式,持久模式或非持久模式
- 持久模式:应该被传送“一次仅仅一次”,这就意味着如果JMS提供者出现故障,该消息也不会丢失,它会在服务器恢复之后再次传递
- 非持久模式:最多会传送一次,这就意味着服务器出现故障,该消息将永远丢失。
- JMSExpiration:可以设置消息在一定时间后过期,默认是永不过期
- 消息过期时间,等于Destination的send方法中的timeToLive值加上发送时刻的GMT时间值。
- 如果timeToLive值等于零,则JMSExpiration被设为零,表示该消息永不过期
- 如果发送后,在消息过期时间之后消息还没有被发送到目的地,则该消息被清除
- JMSPriority:设置消息的优先级,从0-9十个级别,0-4是普通消息,5-9是加急消息
- JMS不要求MQ严格按照这是个优先级发送消息,但必须保证加急消息要先于普通消息到达。默认是4级
- JMSMessageID:唯一识别每个消息的标识有MQ产生
- 消息体:
- 封装具体的消息数据
- 发送和接受的消息体类型必须一致对应
- 5中消息体格式
- TextMessage:普通字符串信息,包含一个string
- MapMessage:一个Map类型的消息,key为string类型,而值为java的基本类型
- BytesMessage:二进制数组消息,包含一个byte[]
- StreamMessage:java数据流消息,用标准流操作来顺序的填充和读取
- ObjectMessage:对象消息,包含一个可序列化的对象
- 消息属性:
- 如果需要除消息头字段以外的值,那么可以使用消息属性
- 识别/去重/重点标注等操作非常有用的方法
- 是以属性名和属性值的形式制定的。可以将属性设为消息头的扩展,属性指定一些消息头没有包括的附加信息,比如可以在属性里指定消息选择器,允许开发者添加有关消息的不透明附加信息,还可以用于暴露消息选择器在消息过滤时使用的数据
- 消息头:
JMS的可靠性
- PERSISTENT:持久性
- 参数设置说明
- 非持久化:messageProducer.setDeliverMode(DeliveryMode.NON_PERSISTENT);
- 当服务器宕机,该消息不存在
- 持久化:messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
- 当服务器宕机,该消息依然存在
- 默认是持久的
- 非持久化:messageProducer.setDeliverMode(DeliveryMode.NON_PERSISTENT);
- 持久化的Queue:是队列默认的传送模式,此模式保证这些消息只被传送一个和成功调用一次。对于这些消息,可靠性是优先考虑的因素。可靠性的另一个重要方面是确保持久性消息传送至目标后,消息服务在向消费者传送他们之前不会丢失这些消息
- 持久化的Topic:
- 先启动订阅再启动生产
- 持久的发布主题生产者
- 持久的订阅主题消费者
- 参数设置说明
- 事务
- producer提交时的事务
- false:只要执行send,就进入到队列中,关闭事务,那第二个签收参数的设置需要有效
- true:先执行send在执行commit,消息才真正的提交到队列中。消息需要批量发送,需要缓冲区处理
- 事务偏生产者/签收偏消费者
- producer提交时的事务
- Acknowledge:签收
- 非事务:
- 自动签收(默认):Session.AUTO_ACKNOWLEDGE
- 手动签收:Session.CLIENT_ACKNOWLEDGE
- 客户端调用acknowledge方法手动签收 message.acknowledge()
- 允许重复消息:Session.DUPS_OK_ACKNOWLEDGE
- 事务:生产事务开启,只有commit后才能将全部消息变为已消费
- 签收和事务的关系
- 在事务性会话中,当一个事务被成功提交,则消息被自动签收,如果事务回滚,则消息会被再次传送
- 非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement mode)
- 非事务:
JMS的点对点总结
点对点模型是基于队列的,生产者发消息到队列,消费者从队列接受消息,队列的存在使得消息的异步传输称为可能。和我们平时给朋友发送短信类似
1.如果在Session关闭时有部分消息已被收到但还没有被签收(acknowledged),那当消费者下次连接到相同的队列时,那些消息还会被再次接受
2.队列可以长久地保存消息知道消费者收到消息。消费者不需要因为担心消息会丢失二时刻和队列保持激活的连接状态,充分体现了异步传输模式的优势。
JMS的发布订阅总结
JMS Pub/Sub模型了定义了如何向一个内容节点发布和订阅消息,这些节点被称作topic
主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe)从主题订阅消息
主题使得消息订阅者和消息发布者保持互相独立,不需要接触即可保证消息的传送
Spring整合ActiveMQ
队列
修改pom.xml文件,添加spring相关依赖
<!-- spring相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.9</version>
</dependency>
<!-- spring-aop相关jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
添加spring配置文件application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启包的自动扫描 -->
<context:component-scan base-package="cn.aixuxi.activemq.springque" />
<!-- 配置生产者 -->
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<!-- 真正可以产生Connection的ConnectionFactory,由对应的JMS服务厂商提供 -->
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="borkerURL" value="tcp://192.168.159.129:61616" />
</bean>
</property>
<property name="maxConnections" value="100" />
</bean>
<!-- 点对点中队列的目的地 -->
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送,接受等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="defaultDestination" ref="destinationQueue" />
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter" />
</property>
</bean>
</beans>
spring整合ActiveMQ的生产者代码
import org.apache.xbean.spring.context.ClassPathXmlApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
@Service
public class SpringMQ_Produce {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringMQ_Produce produce = (SpringMQ_Produce) ctx.getBean("springMQ_Produce");
// produce.jmsTemplate.send(new MessageCreator() {
// @Override
// public Message createMessage(Session session) throws JMSException {
// TextMessage textMessage = session.createTextMessage("spring和ActiveMQ整合");
// return textMessage;
// }
// });
produce.jmsTemplate.send((session)->{
TextMessage textMessage = session.createTextMessage("spring和ActiveMQ整合");
return textMessage;
});
System.out.println("send success!");
}
}
spring整合ActiveMQ的消费者代码
import org.apache.xbean.spring.context.ClassPathXmlApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.jms.core.JmsTemplate;
public class SpringMQ_Consumer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringMQ_Consumer consumer = (SpringMQ_Consumer) ctx.getBean("springMQ_Consumer");
String receive = (String) consumer.jmsTemplate.receiveAndConvert();
System.out.println("消费者收到的消息"+receive);
}
}
主题
修改applicationContext.xml文件,设置主题
<!-- 主题 -->
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送,接受等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory"/>
<property name="defaultDestination" ref="destinationTopic" />
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter" />
</property>
</bean>
消费者与生产者代码不变
spring里实现消费者不启动,直接通过配置监听完成
修改applicationContext.xml文件
<!-- 配置监听程序 -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory" />
<property name="destination" ref="destinationQueue" />
<property name="messageListener" ref="myMessageListener"/>
</bean>
创建MyMessageListener.java监听
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@Component
public class MyMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
if (null != message &&message instanceof TextMessage){
try {
TextMessage textMessage = (TextMessage) message;
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
SpringBoot整合ActiveMQ
创建SpringBoot应用,配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.aixuxi</groupId>
<artifactId>boot_mq_produce</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot_mq_produce</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置文件applicationContext.yml
server:
port: 8000
spring:
activemq:
broker-url: tcp://192.168.159.129:61616 # 自己的MQ服务器地址
user: admin
password: admin
jms:
pub-sub-domain: false # false = Queue true = Topic
# 自己定义队列名称
myqueue: boot-activemq-queue
配置bean
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
@Component
@EnableJms
public class ConfigBean {
@Value("${myqueue}")
private String myQueue;
@Bean //
public Queue queue(){
return new ActiveMQQueue(myQueue);
}
}
队列生产者
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import java.util.UUID;
@Component
public class Queue_Produce {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Queue queue;
public void productMsg(){
jmsMessagingTemplate.convertAndSend(queue,"*****:"+ UUID.randomUUID().toString().substring(0,6));
}
}
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootMqProduceApplication {
public static void main(String[] args) {
SpringApplication.run(BootMqProduceApplication.class, args);
}
}
单元测试类
import boot_mq_produce.produce.Queue_Produce;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import javax.annotation.Resource;
@SpringBootTest(classes = BootMqProduceApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class test {
@Resource
private Queue_Produce queue_produce;
@Test
public void testSend() throws Exception{
queue_produce.productMsg();
}
}
间隔3秒发送消息
-
修改Queue_Produce新增定时投递方法
//间隔3秒定投 @Scheduled(fixedDelay = 3000) public void produceMsgScheduled(){ jmsMessagingTemplate.convertAndSend(queue,"*****:"+ UUID.randomUUID().toString().substring(0,6)); }
-
修改主启动类BootMqProduceApplication
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling public class BootMqProduceApplication { public static void main(String[] args) { SpringApplication.run(BootMqProduceApplication.class, args); } }
队列消费者
需要创建新的微服务,并修改yml配置文件中的端口号
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.TextMessage;
@Component
public class Queue_Consumer {
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage textMessage) throws JMSException {
System.out.println("===消费者收到消息"+textMessage.getText());
}
}
订阅生产者
创建新的微服务,修改yml配置文件中的端口号,并将jms配置中修改为true
-
创建bean
import org.apache.activemq.command.ActiveMQTopic; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.jms.annotation.EnableJms; import org.springframework.stereotype.Component; import javax.jms.Topic; @Component @EnableJms public class ConfigBean { @Value("${myTopic}") private String topicName; @Bean public Topic topic(){ return new ActiveMQTopic(topicName); } }
-
生产者代码
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.scheduling.annotation.Scheduled; import javax.jms.Topic; import java.util.UUID; public class Topic_Produce { @Autowired private JmsMessagingTemplate jmsMessagingTemplate; @Autowired private Topic topic; @Scheduled(fixedDelay = 3000) public void produceTopic() { jmsMessagingTemplate.convertAndSend(topic,"主题消息:"+ UUID.randomUUID().toString().substring(0,6));; } }
主题消费者
import org.springframework.jms.JmsException;
import org.springframework.jms.annotation.JmsListener;
import javax.jms.JMSException;
import javax.jms.TextMessage;
public class Topic_Consumer {
@JmsListener(destination = "${myTopic")
public void receive(TextMessage text) throws JMSException {
System.out.println("消费者收到订阅的主题:"+text.getText());
}
}
小结
对消息中间件有了一定的了解,具体的应用我还没有接触过,只是具备了一些理论。目前要练习的大型项目,要用到RabbitMQ,虽然实现方式不一样,但是底层原理却总是相似的。现在看的这套课程还没学完,再往后是比较高级的课程,我会过两天再统一整理。
就这样。