RabbitMQ-SpringBoot 案例
00、环境搭建
1、IDEA创建生产者工程:springboot-rabbitmq-producer
2、IDEA创建消费者工程:springboot-rabbitmq-consumer
3、生产者和消费者分别引入spring-boot-rabbitmq的依赖和配置连接信息
4、进行消息的分发和测试
5、查看和观察web控制台的状况
1、首先使用IDEA创建SpringBoot项目(步骤略)
2、同时引入如下依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
3、生产者:application.properties文件配置
server.port=8080
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
4、生产者:application.properties文件配置
server.port=8082
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
01、Simple 模式
1、创建队列(生产者消费者同时定义最好)
package com.example.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 简单队列模式
**/
@Configuration
public class SimpleRabbitConfig {
@Bean
public Queue simpleQueue() {
/*
* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
* exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
* autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
* return new Queue("TestDirectQueue",true,true,false);
* 一般设置一下队列的持久化就好,其余两个就是默认false
*/
return new Queue("simple.queue", true);
}
}
2、生产者发送消息
/**
* 简单模式
*/
@Test
public void simpleSend() {
// 简单对列的情况下routingKey即为Q名
this.rabbitTemplate.convertAndSend("simple.queue", "点对点Simple模式测试");
}
3、消费者
package com.example.service;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class SimpleConsumer {
//监听的队列名
@RabbitListener(queues = "simple.queue")
public void processOne(String name) {
System.out.println("simple.queue 收到消息:" + name);
}
}
4、消费者收到测试结果:
simple.queue 收到消息:点对点Simple模式测试
02、Work 模式
02-1、轮询分发模式
轮询分发、平均消费模式:2个消费者队列,轮训获取队列里的消息
1、创建队列(生产者消费者同时定义最好)
package com.example.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 工作队列模式
**/
@Configuration
public class WorkRabbitConfig {
@Bean
public Queue workQueue(){
/*
* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
* exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
* autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
* return new Queue("TestDirectQueue",true,true,false);
* 一般设置一下队列的持久化就好,其余两个就是默认false
*/
return new Queue("work.queue",true);
}
}
2、生产者发送消息
/**
* 工作队列模式
*/
@Test
public void workSend() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work.queue", "工作模式队列:" + i);
}
}
3、消费者-1(慢消费者)
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 慢消费者,模拟性能比较差的服务器
*/
@Component
public class WorkConsumer1 {
@RabbitListener(queues = "work.queue")
public void processOne(String name, Channel channel, Message message) throws Exception {
// 模拟执行每次任务需要1秒
Thread.sleep(1000);
System.out.println("WorkConsumer1-慢消费者 收到消息:" + name);
}
}
4、消费者-2(快消费者)
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 快消费者,模拟性能比较好的服务器
*/
@Component
public class WorkConsumer2 {
@RabbitListener(queues = "work.queue")
public void processTwo(String name, Channel channel, Message message) throws Exception {
System.out.println("WorkConsumer2-快消费者 收到消息:" + name);
}
}
5、消费者收到测试结果:
WorkConsumer2-快消费者 收到消息:工作模式队列:1
WorkConsumer2-快消费者 收到消息:工作模式队列:3
WorkConsumer2-快消费者 收到消息:工作模式队列:5
WorkConsumer2-快消费者 收到消息:工作模式队列:7
WorkConsumer2-快消费者 收到消息:工作模式队列:9
WorkConsumer1-慢消费者 收到消息:工作模式队列:0
WorkConsumer1-慢消费者 收到消息:工作模式队列:2
WorkConsumer1-慢消费者 收到消息:工作模式队列:4
WorkConsumer1-慢消费者 收到消息:工作模式队列:6
WorkConsumer1-慢消费者 收到消息:工作模式队列:8
可以看到效果,两个消费者都分别获取到了5条消息,但是问题来了,消费者-2 很快的消费完了自己的5条消息,然后就闲置了。但是消费者-1执行很慢,执行5条消息,就需要大于5秒的时间,这样就造成了性能浪费。我们应该让RabbitMQ智能一些,给每个消费者每次只给一个消息,当确认消息完成之后再下发另一个消息,这样就可以能者多劳了。
02-2、能者多劳模式
确认机制和能者多劳消费模式:2个消费者队列,手动确认接收消息,消费者1加了睡眠时间,用来模拟消费消息慢
1、修改配置文件增加配置
# 在单个请求中处理的消息个数,每个消费者每次监听时可拉取处理的消息数量
spring.rabbitmq.listener.simple.prefetch=1
# 消费手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
**备注:**实际上不加手动ack消息确认也可以实现。不过 一般来说会两个配置同时加上
2、生产者发送消息不变
/**
* 工作队列模式
*/
@Test
public void workSend() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work.queue", "工作模式队列:" + i);
}
}
2、消费者-1(慢消费者)
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 慢消费者,模拟性能比较差的服务器
*/
@Component
public class WorkConsumer1 {
@RabbitListener(queues = "work.queue")
public void processOne(String name, Channel channel, Message message) throws Exception {
// 模拟执行每次任务需要1秒
Thread.sleep(1000);
System.out.println("WorkConsumer1-慢消费者 收到消息:" + name);
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}
3、消费者-2(快消费者)
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 快消费者,模拟性能比较好的服务器
*/
@Component
public class WorkConsumer2 {
@RabbitListener(queues = "work.queue")
public void processTwo(String name, Channel channel, Message message) throws Exception {
System.out.println("WorkConsumer2-快消费者 收到消息:" + name);
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}
4、消费者收到测试结果:
WorkConsumer2-快消费者 收到消息:工作模式队列:1
WorkConsumer2-快消费者 收到消息:工作模式队列:2
WorkConsumer2-快消费者 收到消息:工作模式队列:3
WorkConsumer2-快消费者 收到消息:工作模式队列:4
WorkConsumer2-快消费者 收到消息:工作模式队列:5
WorkConsumer2-快消费者 收到消息:工作模式队列:6
WorkConsumer2-快消费者 收到消息:工作模式队列:7
WorkConsumer2-快消费者 收到消息:工作模式队列:8
WorkConsumer2-快消费者 收到消息:工作模式队列:9
WorkConsumer1-慢消费者 收到消息:工作模式队列:0
可以看到,已经达到我们想要的效果了,消费-2者消费了9条消息,而消费者-1 只消费了一条消息。
03、Fanout 模式
1、创建队列、交换机、绑定关系(生产者消费者同时定义最好)
package com.example.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Fanout 订阅模式
**/
@Configuration
public class FanoutRabbitMQConfig {
/**
* 声明队列
*/
@Bean
public Queue fanoutQueueOne() {
/*
* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
* exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
* autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
* return new Queue("TestFanoutQueue",true,true,false);
* 一般设置一下队列的持久化就好,其余两个就是默认false
*/
return new Queue("fanout.queue.one", true);
}
@Bean
public Queue fanoutQueueTwo() {
return new Queue("fanout.queue.two", true);
}
@Bean
public Queue fanoutQueueThree() {
return new Queue("fanout.queue.three", true);
}
/**
* 创建 Fanout 模式交换机
*/
@Bean
public FanoutExchange fanoutExchange() {
/*
* param1: 交换机名称
* param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
* param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
*/
return new FanoutExchange("fanout_exchange", true, false);
}
/**
* 绑定队列(不用指定routing key),参数名字要和bean名字一致
*/
@Bean
public Binding bindingFanout1() {
return BindingBuilder.bind(fanoutQueueOne()).to(fanoutExchange());
}
@Bean
public Binding bindingFanout2() {
return BindingBuilder.bind(fanoutQueueTwo()).to(fanoutExchange());
}
@Bean
public Binding bindingFanout3() {
return BindingBuilder.bind(fanoutQueueThree()).to(fanoutExchange());
}
}
2、生产者发送消息
/**
* 发布订阅模式
*/
@Test
public void fanoutSend() {
// 发布订阅模式下routingKey为空
this.rabbitTemplate.convertAndSend("fanout_exchange","","Fanout-发布订阅模式测试");
}
3、消费者
package com.example.service;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class FanoutConsumer {
@RabbitListener(queues = "fanout.queue.one")
public void processEmail(String name) {
System.out.println("fanout.queue.one 收到消息:" + name);
}
@RabbitListener(queues = "fanout.queue.two")
public void processSms(String name) {
System.out.println("fanout.queue.two 收到消息:" + name);
}
@RabbitListener(queues = "fanout.queue.three")
public void processWeiXin(String name) {
System.out.println("fanout.queue.three 收到消息:" + name);
}
}
5、消费者收到测试结果:
fanout.queue.one 收到消息:Fanout-发布订阅模式测试
fanout.queue.three 收到消息:Fanout-发布订阅模式测试
fanout.queue.two 收到消息:Fanout-发布订阅模式测试
04、Direct 模式
1、创建队列、交换机、绑定关系
package com.example.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Direct 路由模式
**/
@Configuration
public class DirectRabbitMQConfig {
/**
* 声明队列
*/
@Bean
public Queue directQueueOne() {
/*
* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
* exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
* autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
* return new Queue("TestDirectQueue",true,true,false);
* 一般设置一下队列的持久化就好,其余两个就是默认false
*/
return new Queue("direct.queue.one", true);
}
@Bean
public Queue directQueueTwo() {
return new Queue("direct.queue.two", true);
}
@Bean
public Queue directQueueThree() {
return new Queue("direct.queue.three", true);
}
/**
* 创建 Direct 模式交换机
*/
@Bean
public DirectExchange directExchange() {
/*
* param1: 交换机名称
* param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
* param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
*/
return new DirectExchange("direct_exchange", true, false);
}
/**
* 绑定队列(不用指定routing key),参数名字要和bean名字一致
*/
@Bean
public Binding bindingDirect1() {
return BindingBuilder.bind(directQueueOne()).to(directExchange()).with("1");
}
@Bean
public Binding bindingDirect2() {
return BindingBuilder.bind(directQueueTwo()).to(directExchange()).with("2");
}
@Bean
public Binding bindingDirect3() {
return BindingBuilder.bind(directQueueThree()).to(directExchange()).with("3");
}
}
2、生产者发送消息
/**
* 路由模式
*/
@Test
public void directSend() {
// 路由模式,分别对不同的routingKey发送消息
this.rabbitTemplate.convertAndSend("direct_exchange","1","Direct-One:" + new Date());
this.rabbitTemplate.convertAndSend("direct_exchange","2","Direct-Two:" + new Date());
this.rabbitTemplate.convertAndSend("direct_exchange","3","Direct-Three:" + new Date());
}
3、消费者接收消息
package com.example.service;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class DirectConsumer {
@RabbitListener(queues = "direct.queue.one")
public void processEmail(String name) {
System.out.println("direct.queue.one 收到消息:" + name);
}
@RabbitListener(queues = "direct.queue.two")
public void processSms(String name) {
System.out.println("direct.queue.two 收到消息:" + name);
}
@RabbitListener(queues = "direct.queue.three")
public void processWeiXin(String name) {
System.out.println("direct.queue.three 收到消息:" + name);
}
}
4、消费者收到测试结果:
direct.queue.one 收到消息:Direct-One测试
direct.queue.two 收到消息:Direct-Two测试
direct.queue.three 收到消息:Direct-Three测试
05、Topic 模式
1、创建队列、交换机、绑定关系
package com.example.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Topic 广播模式
**/
@Configuration
public class TopicRabbitMQConfig {
/**
* 声明队列
*/
@Bean
public Queue topicQueueOne() {
/*
* durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
* exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
* autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
* return new Queue("TestQueue",true,true,false);
* 一般设置一下队列的持久化就好,其余两个就是默认false
*/
return new Queue("topic.queue.one", true);
}
@Bean
public Queue topicQueueTwo() {
return new Queue("topic.queue.two", true);
}
@Bean
public Queue topicQueueThree() {
return new Queue("topic.queue.three", true);
}
/**
* 创建 topic 模式交换器
*/
@Bean
public TopicExchange topicExchange(){
/*
* param1: 交换机名称
* param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
* param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
*/
return new TopicExchange("topic_exchange", true, false);
}
/**
* 绑定队列
*/
@Bean
public Binding bindingTopicOne(){
return BindingBuilder.bind(topicQueueOne()).to(topicExchange()).with("#.error");
}
@Bean
public Binding bindingTopicTwo(){
return BindingBuilder.bind(topicQueueTwo()).to(topicExchange()).with("#.log");
}
@Bean
public Binding bindingTopicThree(){
return BindingBuilder.bind(topicQueueThree()).to(topicExchange()).with("good.#.timer");
}
}
2、生产者发送消息
/**
* 广播模式
*/
@Test
public void topicSend() {
rabbitTemplate.convertAndSend("topic_exchange","test.error","主题模式测试");
rabbitTemplate.convertAndSend("topic_exchange","test.log","主题模式测试");
rabbitTemplate.convertAndSend("topic_exchange","good.test.timer","主题模式测试");
}
3、消费者接收消息
package com.example.service;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class TopicConsumer {
@RabbitListener(queues = "topic.queue.one")
public void processOne(String name) {
System.out.println("topic.queue.one 收到消息:" + name);
}
@RabbitListener(queues = "topic.queue.two")
public void processTwo(String name) {
System.out.println("topic.queue.tw 收到消息:" + name);
}
@RabbitListener(queues = "topic.queue.three")
public void processThree(String name) {
System.out.println("topic.queue.three 收到消息:" + name);
}
}
4、消费者收到测试结果:
topic.queue.one 收到消息:主题模式测试
topic.queue.two 收到消息:主题模式测试
topic.queue.three 收到消息:主题模式测试
06、Header 模式
1、创建队列、交换机、绑定关系
package com.example.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* Header 模式
*/
@Configuration
public class HeaderRabbitMQConfig {
/**
* Headers 模式
* 设置header attribute参数类型的交换机,相较于 direct 和 topic 固定地使用 routing_key ,
* headers 则是一个自定义匹配规则的类型. 在队列与交换器绑定时, 会设定一组键值对规则,
* 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列
*/
@Bean
public Queue headerQueueOne(){
return new Queue("header.queue.one",true);
}
@Bean
public Queue headerQueueTwo(){
return new Queue("header.queue.two",true);
}
/**
* 创建 Header 模式交换机
*/
@Bean
public HeadersExchange headersExchange(){
/*
* param1: 交换机名称
* param2: durable:是否持久化,RabbitMQ关闭后,没持久化Exchange将被清除
* param3: autoDelete:是否自动删除,,如果没有与之绑定的Queue直接删除
*/
return new HeadersExchange("header_exchange", true, false);
}
@Bean
public Binding headerBinding1(){
Map<String, Object> map = new HashMap<>();
map.put("header1","value1");
map.put("header11","value11");
// 匹配header中某一个即可
return BindingBuilder.bind(headerQueueOne()).to(headersExchange()).whereAny(map).match();
}
@Bean
public Binding headerBinding2(){
Map<String, Object> map = new HashMap<>();
map.put("header2","value2");
map.put("header22","value22");
// 必须全部匹配header
return BindingBuilder.bind(headerQueueTwo()).to(headersExchange()).whereAll(map).match();
}
}
2、生产者发送消息
/**
* Headers模式
*/
@Test
public void headersSend() {
MessageProperties messageProperties1 = new MessageProperties();
messageProperties1.setHeader("header1","value1");
messageProperties1.setHeader("header11","value11");
Message message1 = new Message(("Header模式—单个匹配").getBytes(), messageProperties1);
rabbitTemplate.convertAndSend("header_exchange","",message1);
MessageProperties messageProperties2 = new MessageProperties();
messageProperties2.setHeader("header2","value2");
messageProperties2.setHeader("header22","value22");
Message message2 = new Message(("Header模式—全部匹配").getBytes(), messageProperties2);
rabbitTemplate.convertAndSend("header_exchange","",message2);
}
3、消费者接收消息
package com.example.service;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class HeadersConsumer {
@RabbitListener(queues = "header.queue.one")
public void processOne(byte[] bytes) {
System.out.println("header.queue.one 收到消息:" + new String(bytes));
}
@RabbitListener(queues = "header.queue.two")
public void processTwo(byte[] bytes) {
System.out.println("header.queue.two 收到消息:" + new String(bytes));
}
}
4、消费者收到测试结果:
header.queue.one 收到消息:Header模式—单个匹配
header.queue.two 收到消息:Header模式—全部匹配