前言
记录我在 Spring 和 Springboot 中 整合使用 RabbitMQ 的项目示例。一、Spring 整合 RabbitMQ
spring 整合 RabbitMQ 就是把创建队列、创建交换机、绑定路由 key 等等的所有公共操作都交给spring容器来进行,我们的代码只进行消息的产生发送和消费。
我们把所有的配置都放在 整合配置文件中 进行配置。没有业务代码,最后通过测试类,模拟发送消息。
1.1 生产者
1.1.1 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-rabbitmq-producer</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 引入依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
</project>
1.1.2 RabbitMQ配置信息
rabbitmq.host=192.168.191.130
rabbitmq.port=5672
rabbitmq.username=admin
rabbitmq.password=admin
rabbitmq.virtual-host=/xzk
1.1.3 整合配置文件
<?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:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 引入 rabbitmq 的配置文件 -->
<context:property-placeholder location="classpath:rabbitmq/rabbitmq.properties"/>
<!-- 定义 RabbitMQ 连接工厂 -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!-- 定义交换机、队列 -->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 定义持久化队列,不存在则自动创建 -->
<!-- 简单模式下的队列 -->
<rabbit:queue id="spring_rabbitmq_simple_queue" name="spring_rabbitmq_simple_queue" auto-declare="true"/>
<!-- 广播模式(发布订阅模式)-->
<!-- 队列 -->
<rabbit:queue id="spring_rabbitmq_fanout_queue_01" name="spring_rabbitmq_fanout_queue_01" auto-declare="true"/>
<rabbit:queue id="spring_rabbitmq_fanout_queue_02" name="spring_rabbitmq_fanout_queue_02" auto-declare="true"/>
<!-- 将上面的两个队列绑定到交换机上 -->
<rabbit:fanout-exchange id="spring_rabbitmq_fanout_exchange" name="spring_rabbitmq_fanout_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_rabbitmq_fanout_queue_01"/>
<rabbit:binding queue="spring_rabbitmq_fanout_queue_02"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- 通配符模式 -->
<rabbit:queue id="spring_rabbitmq_topic_queue_01" name="spring_rabbitmq_topic_queue_01" auto-declare="true"/>
<rabbit:queue id="spring_rabbitmq_topic_queue_02" name="spring_rabbitmq_topic_queue_02" auto-declare="true"/>
<rabbit:queue id="spring_rabbitmq_topic_queue_03" name="spring_rabbitmq_topic_queue_03" auto-declare="true"/>
<!-- 绑定队列交换机 -->
<rabbit:topic-exchange id="spring_rabbitmq_topic_exchange" name="spring_rabbitmq_topic_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding pattern="sjq.*" queue="spring_rabbitmq_topic_queue_01"/>
<rabbit:binding pattern="sjq.#" queue="spring_rabbitmq_topic_queue_02"/>
<rabbit:binding pattern="xzk.#" queue="spring_rabbitmq_topic_queue_03"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<!-- 定义 RabbitTemplate 对象,方便代码中操作数据 -->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
1.1.4 测试类
package com.kaikeba.spring.rabbitmq;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Spring 整合 RabbitMQ 测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/spring-rabbitmq.xml") // classpath: 与后面的路径之间不能有空格 classpath: spring/spring-rabbitmq.xml 就会报错
public class SpringRabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 只发送队列消息
* 使用默认交换机,类型为 Direct
* 交换机名称与队列同名
*/
@Test
public void simpleQueueTest() {
// 路由键与队列同名
rabbitTemplate.convertAndSend("spring_rabbitmq_simple_queue", "直接发送到队列中的消息!!!");
}
/**
* 发送广播信息
* 交换机类型为 fanout
* 绑定到该交换机上的所有队列都能收到消息
*/
@Test
public void fanoutQueueTest() {
rabbitTemplate.convertAndSend("spring_rabbitmq_fanout_exchange", "", "fanout 广播模式的消息,绑定到这个交换机上的队列都能收到!!");
}
/**
* 通配符模式
* 交换机类型为 Topic
* 匹配路由键的通配符,* 只匹配一个单词, # 匹配若干个(一个或多个)
* 绑定到该交换机的匹配队列,能够接收到对应的消息。
*/
@Test
public void topicQueueTest() {
rabbitTemplate.convertAndSend("spring_rabbitmq_topic_exchange", "sjq.first", "routing key is sjq.first send message test");
rabbitTemplate.convertAndSend("spring_rabbitmq_topic_exchange", "sjq.second.lll","routing key is sjq.second.lll .....");
rabbitTemplate.convertAndSend("spring_rabbitmq_topic_exchange","xzk.cn.com","路由key是xzk.xn.com,jiandancieh");
}
}
1.1.5 测试结果
1.1.5.1 交换机、队列
spring容器启动的时候,就会将配置好的交换机和队列都生成出来。
- 交换机
- 队列
- 交换机队列绑定关系
1.1.5.2 队列中的消息
- 队列
- 广播模式
- 通配符模式
1.2 消费者
1.2.1 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-rabbitmq-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
</project>
1.2.2 RabbitMQ 配置
rabbitmq.host=192.168.191.130
rabbitmq.port=5672
rabbitmq.username=admin
rabbitmq.password=admin
rabbitmq.virtual-host=/xzk
1.2.3 spring-rabbitmq配置
<?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:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 引入 rabbitmq 的配置文件 -->
<context:property-placeholder location="classpath:rabbitmq/rabbitmq.properties"/>
<!-- 定义 RabbitMQ 连接工厂 -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!-- 定义 RabbitTemplate 对象,方便代码中操作数据 -->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
<!-- 监听器 -->
<bean id="springRabbitSimpleQueueListener" class="com.kaikeba.spring.rabbit.listener.SpringRabbitSimpleQueueListener"/>
<bean id="springRabbitFanoutQueue01" class="com.kaikeba.spring.rabbit.listener.SpringRabbitFanoutQueue01"/>
<bean id="springRabbitFanoutQueue02" class="com.kaikeba.spring.rabbit.listener.SpringRabbitFanoutQueue02"/>
<bean id="springRabbitTopicQueue01" class="com.kaikeba.spring.rabbit.listener.SpringRabbitTopicQueue01"/>
<bean id="springRabbitTopicQueue02" class="com.kaikeba.spring.rabbit.listener.SpringRabbitTopicQueue02"/>
<bean id="springRabbitTopicQueue03" class="com.kaikeba.spring.rabbit.listener.SpringRabbitTopicQueue03"/>
<!-- 监听队列 -->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
<rabbit:listener ref="springRabbitSimpleQueueListener" queue-names="spring_rabbitmq_simple_queue"/>
<rabbit:listener ref="springRabbitFanoutQueue01" queue-names="spring_rabbitmq_fanout_queue_01"/>
<rabbit:listener ref="springRabbitFanoutQueue02" queue-names="spring_rabbitmq_fanout_queue_02"/>
<rabbit:listener ref="springRabbitTopicQueue01" queue-names="spring_rabbitmq_topic_queue_01"/>
<rabbit:listener ref="springRabbitTopicQueue02" queue-names="spring_rabbitmq_topic_queue_02"/>
<rabbit:listener ref="springRabbitTopicQueue03" queue-names="spring_rabbitmq_topic_queue_03"/>
</rabbit:listener-container>
</beans>
1.2.4 测试类
消费者的测试类,目的只是启动 spring 容器。让对应队列的监听器开始工作,所以我们写个死循环。
package com.kaikeba.spring.rabbit.listener;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/spring-rabbitmq.xml")
public class AllTest {
/**
* 没有代码,仅仅是为了启动容器
*/
@Test
public void test() {
while (true) {
}
}
}
1.2.5 监听类
我们每个监听类除了类名之外,完全相同,所以我们只放一个监听类的代码:
package com.kaikeba.spring.rabbit.listener;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import java.io.UnsupportedEncodingException;
/**
* 接收消息:队列监听器
*/
public class SpringRabbitSimpleQueueListener implements MessageListener {
public void onMessage(Message message) {
try {
String msg = new String(message.getBody(), "UTF-8");
System.out.println("接收路由名为:" + message.getMessageProperties().getReceivedExchange());
System.out.println("路由 key 为:" + message.getMessageProperties().getReceivedRoutingKey());
System.out.println("队列名为:" + message.getMessageProperties().getConsumerQueue());
System.out.println("接收的消息为:" + msg);
System.out.println("==================================================================================");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
1.2.6 测试结果
控制台查看监听器的输出:
二、springboot 整合 RabbitMQ
2.1 介绍
在 Spring 项目中,可以使用 Spring-Rabbit 去操作 RabbitMQ
尤其是在 Springboot 项目中只需要引入对应的 amqp 启动依赖就可以使用, 方便的使用RabbitTemplate发 送消息,使用注解接收消息。
- 生产者工程:
- 在 yml 或 properties 配置文件配置 RabbitMQ 的相关信息;
- 在生产只工程中编写配置类,创建交换机、队列 并 进行绑定;
- 注入 RabbitTemplate 对象, 通过RabbitTemplate对象发送消息到交换机 。
消费者工程:
- 在 yml 或 properties 配置文件配置 RabbitMQ 的相关信息;
- 创建消息处理类,用于接收队列中的消息并进行处理 。
2.2 生产者工程
2.2.1 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 http://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.1.4.RELEASE</version>
</parent>
<groupId>org.example</groupId>
<artifactId>springboot-rabbitmq-producer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
2.2.2 RabbitMQ配置
spring.rabbitmq.host=192.168.191.130
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.virtual-host=/xzk
2.2.3 配置类
package com.kaikeba.springboot.rabbitmq.configuration;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitMQ 配置文件
*/
@Configuration
public class RabbitMQConfiguration {
//交换机名称
public static final String ITEM_TOPIC_EXCHANGE = "springboot_item_topic_exchange";
//队列名称
public static final String ITEM_QUEUE = "springboot_item_queue";
/**
* 声明交换机
*/
@Bean("itemTopicExchange")
public Exchange topicExchange() {
return ExchangeBuilder.topicExchange(ITEM_TOPIC_EXCHANGE).durable(true).build();
}
/**
* 声明队列
*/
@Bean("itemQueue")
public Queue itemQueue() {
return QueueBuilder.durable(ITEM_QUEUE).build();
}
/**
* 绑定交换机和队列
*/
@Bean
public Binding itemQueueExchange(@Qualifier("itemQueue") Queue queue, @Qualifier("itemTopicExchange") Exchange itemTopicExchange) {
return BindingBuilder.bind(queue).to(itemTopicExchange).with("item.#").noargs();
}
}
2.2.4 启动类
package com.kaikeba.springboot.rabbitmq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootRabbitMQProducerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootRabbitMQProducerApplication.class, args);
}
}
2.2.5 测试类
package com.kaikeba.springboot.rabbitmq;
import com.kaikeba.springboot.rabbitmq.configuration.RabbitMQConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQProducerTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void test() {
rabbitTemplate.convertAndSend(RabbitMQConfiguration.ITEM_TOPIC_EXCHANGE, "item.insert", "商品新增,routing key 为item.insert");
rabbitTemplate.convertAndSend(RabbitMQConfiguration.ITEM_TOPIC_EXCHANGE, "item.update", "商品修改,routing key 为item.update");
rabbitTemplate.convertAndSend(RabbitMQConfiguration.ITEM_TOPIC_EXCHANGE, "item.delete", "商品删除,routing key 为item.delete");
}
}
2.2.6 测试结果
-
交换机
-
绑定关系
-
队列
-
消息
2.3 消费者工程
2.3.1 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 http://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.1.4.RELEASE</version>
</parent>
<groupId>org.example</groupId>
<artifactId>springboot-rabbitmq-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
2.3.2 RabbitMQ 配置
spring.rabbitmq.host=192.168.191.130
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.virtual-host=/xzk
2.3.3 监听类
package com.kaikeba.springboot.rabbitmq.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class QueueListener {
@RabbitListener(queues = "springboot_item_queue")
public void listenQueue(String message) {
System.out.println("消费者接收到的消息为:" + message);
}
}
2.3.4 启动类
package com.kaikeba.springboot.rabbitmq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootRabbitMQConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootRabbitMQConsumerApplication.class, args);
}
}
2.3.5 测试类
测试类的目的是启动项目,使得监听类正常工作,所以没有逻辑代码:
package com.kaikeba.springboot.rabbitmq;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootRabbitMQConsumerTest {
@Test
public void test() {
while (true) {
}
}
}