文章目录
- [1. 创建 SpringBoot 项目](#1. 创建 SpringBoot 项目)
- [2. 工作队列模式](#2. 工作队列模式)
-
- [2.1 编写生产者代码](#2.1 编写生产者代码)
- [2.2 编写消费者代码](#2.2 编写消费者代码)
- [2.3 编写多个消费者代码](#2.3 编写多个消费者代码)
- [3. Publish/Subscribe(发布订阅模式)](#3. Publish/Subscribe(发布订阅模式))
-
- [3.1 编写生产者代码](#3.1 编写生产者代码)
- [3.2 编写消费者代码](#3.2 编写消费者代码)
- [3.3 运行程序](#3.3 运行程序)
- [4. Routing(路由模式)](#4. Routing(路由模式))
-
- [4.1 编写生产者代码](#4.1 编写生产者代码)
- [4.2 编写消费者代码](#4.2 编写消费者代码)
- [4.3 运行程序](#4.3 运行程序)
- [5. 通配符模式](#5. 通配符模式)
-
- [5.1 编写生产者代码](#5.1 编写生产者代码)
- [5.2 编写消费者代码](#5.2 编写消费者代码)
- [5.3 运行程序](#5.3 运行程序)
对于 RabbitMQ 开发,Spring 也提供了一些便利。Spring 和 RabbitMQ 的官方文档对此均有介绍。
下面来看如何基于 SpringBoot 进行 RabbitMQ 的开发。咱们只演示部分常用的工作模式:
- 工作队列模式(Work Queues)
- 发布订阅模式(Publish / Subscribe)
- 路由模式(Routing)
- 通配符模式(Topics)
1. 创建 SpringBoot 项目
新建一个空的项目如下所示:

然后添加依赖:

创建好以后,把下面几个没有用的给删除掉

并且依赖已经是添加好了的

然后添加 rabbitmq 的服务配置,这里使用 yml 的格式
写法一
yml
#配置RabbitMQ的基本信息
spring:
rabbitmq:
host:
port: 5672 #默认为5672
username:
password:
virtual-host: #默认值为/
或者还有下面这种写法(推荐)
yml
#配置RabbitMQ的基本信息
rabbitmq:
#amqp://username:password@Ip:port/virtual-host
addresses: amqp://edison:edison@IP:5672/my_app_vhost
注意:RabbitMQ 的通信端口是 5672,管理后台端口是 15672。
下面就可以开始完成代码的编写了。
2. 工作队列模式
步骤:
- 1、引入依赖
- 2、编写 yml 配置,基本信息配置
- 3、编写生产者代码
- 4、编写消费者代码
- a、定义监听类,使用 @RabbitListener 注解完成队列监听
- 5、运行观察结果
2.1 编写生产者代码
先把队列名称定义为一个常量
java
public static final String WORK_QUEUE = "workQueue";
声明队列
java
package com.edison.rabbitmq.config;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// 1. 工作模式队列
@Bean("workQueue")
public Queue workQueue() {
return QueueBuilder.durable(Constants.WORK_QUEUE).build();
}
}
为了方便测试,我们通过接口来发送消息
java
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/producer")
@RestController
public class ProducerController {
@Autowired
private RabbitTemplate rabbitTemplate; // 可以理解为rabbitmq客户端
@RequestMapping("/work")
public String work() {
// 使用内置交换机, RoutingKey和队列名称一致
rabbitTemplate.convertAndSend("", Constants.WORK_QUEUE, "hello spring amqp: work...");
return "发送成功";
}
}
运行代码

然后在浏览器测试接口

同时从日志中也可以看到创建了一个新的连接

并且管理后台已经有队列信息了

由此可知,当我们把 SpringBoot 程序启动以后,它其实并没有帮我们创建队列,而是在我们发送消息的时候,才会进行创建。
2.2 编写消费者代码
定义监听类
java
package com.edison.rabbitmq.listener;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class WorkListener {
@RabbitListener(queues = Constants.WORK_QUEUE)
public void queueListener(Message message) {
System.out.println("["+Constants.WORK_QUEUE+"] 接收到消息: "+message);
}
}
@RabbitListener 是 Spring 框架中用于监听 RabbitMQ 队列的注解,通过使用这个注解,可以定义一个方法,以便从 RabbitMQ 队列中接收消息。该注解支持多种参数类型,这些参数类型代表了从 RabbitMQ 接收到的消息和相关信息。
以下是一些常用的参数类型:
- 1、String:返回消息的内容
- 2、Message(
org.springframework.amqp.core.Message):Spring AMQP 的 Message 类,返回原始的消息体以及消息的属性,如消息 ID,内容,队列信息等。 - 3、Channel(
com.rabbitmq.client.Channel):RabbitMQ 的通道对象,可以用于进行更高级的操作,如手动确认消息。
消费者测试,打印消息内容

消息内容如下所示:
[workQueue] 接收到消息: (Body:'hello spring amqp: work...' MessageProperties [headers={}, contentType=text/plain,
contentEncoding=UTF-8,
contentLength=0,
receivedDeliveryMode=PERSISTENT,
priority=0,
redelivered=true,
receivedExchange=,
receivedRoutingKey=workQueue,
deliveryTag=1,
consumerTag=amq.ctag-FXo4xTJQt74J4YDbwOS0GQ, consumerQueue=workQueue])
并且此时在管理后台可以看到,消息已经被消费了。
2.3 编写多个消费者代码
代码如下所示:
java
package com.edison.rabbitmq.listener;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class WorkListener {
@RabbitListener(queues = Constants.WORK_QUEUE)
public void queueListener1(Message message) {
System.out.println("listener 1 ["+Constants.WORK_QUEUE+"] 接收到消息: "+message);
}
@RabbitListener(queues = Constants.WORK_QUEUE)
public void queueListener2(Message message) {
System.out.println("listener 2 ["+Constants.WORK_QUEUE+"] 接收到消息: "+message);
}
}
然后修改生产者代码,让其一次性发送多条消息
java
package com.edison.rabbitmq.controller;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/producer")
@RestController
public class ProducerController {
@Autowired
private RabbitTemplate rabbitTemplate; // 可以理解为rabbitmq客户端
@RequestMapping("/work")
public String work() {
for (int i = 0; i < 10; i ++)
{
// 使用内置交换机, RoutingKey和队列名称一致
rabbitTemplate.convertAndSend("", Constants.WORK_QUEUE, "hello spring amqp: work...");
}
return "发送成功";
}
}
然后运行结果,如下所示:

3. Publish/Subscribe(发布订阅模式)
在发布/订阅模型中,多了一个Exchange角色。
Exchange常见有三种类型,分别代表不同的路由规则:
- a) Fanout:广播,将消息交给所有绑定到交换机的队列(Publish/Subscribe模式)
- b) Direct:定向,把消息交给符合指定routing key的队列(Routing模式)
- c) Topic:通配符,把消息交给符合routing pattern(路由模式)的队列(Topics模式)
我们先来看 Fanout 广播路由规则。
3.1 编写生产者代码
和简单模式的区别是:需要创建交换机,并且绑定队列和交换机。
定义队列和交换机
java
// 发布订阅模式
public static final String FANOUT_QUEUE1 = "fanout.queue1";
public static final String FANOUT_QUEUE2 = "fanout.queue2";
public static final String FANOUT_EXCHANGE = "fanout.exchange";
声明队列和交换机
java
// 2. 发布订阅模式
// 声明2个队列, 观察是否两个队列都收到了消息
@Bean("fanoutQueue1")
public Queue fanoutQueue1() {
return QueueBuilder.durable(Constants.FANOUT_QUEUE1).build();
}
@Bean("fanoutQueue2")
public Queue fanoutQueue2() {
return QueueBuilder.durable(Constants.FANOUT_QUEUE2).build();
}
// 声明交换机
@Bean("fanoutExchange")
public FanoutExchange fanoutExchange() {
return ExchangeBuilder.fanoutExchange(Constants.FANOUT_EXCHANGE).durable(true).build();
}
绑定队列和交换机
java
// 声明交换机和队列的绑定
@Bean("fanoutQueueBinding1")
public Binding fanoutQueueBinding1(@Qualifier("fanoutExchange") FanoutExchange fanoutExchange, @Qualifier("fanoutQueue1") Queue queue) {
return BindingBuilder.bind(queue).to(fanoutExchange);
}
@Bean("fanoutQueueBinding2")
public Binding fanoutQueueBinding2(@Qualifier("fanoutExchange") FanoutExchange fanoutExchange, @Qualifier("fanoutQueue1") Queue queue) {
return BindingBuilder.bind(queue).to(fanoutExchange);
}
然后使用接口发送消息
java
@RequestMapping("/fanout")
public String fanout() {
// routingKey为空, 表示所有队列都可以收到消息
rabbitTemplate.convertAndSend(Constants.FANOUT_EXCHANGE, "", "hello spring amqp: fanout...");
return "发送成功";
}
然后启动程序,观察结果,可以看到队列已经和交换机绑定好了

然后再通过接口发送消息,可以看到此时队列中已经有了消息

3.2 编写消费者代码
交换机和队列的绑定关系及声明已经在生产方写完,所以消费者不需要再写了。
定义监听类,处理接收到的消息即可。
java
package com.edison.rabbitmq.listener;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class FanoutListener {
@RabbitListener(queues = Constants.FANOUT_QUEUE1)
public void queueListener1(String message) {
System.out.println("队列["+Constants.FANOUT_QUEUE1+"] 接收到消息: "+message);
}
@RabbitListener(queues = Constants.FANOUT_QUEUE2)
public void queueListener2(String message) {
System.out.println("队列["+Constants.FANOUT_QUEUE2+"] 接收到消息: "+message);
}
}
3.3 运行程序
先运行项目,调用接口 http://127.0.0.1:8080/producer/fanout 发送消息
然后监听类收到消息,并打印

4. Routing(路由模式)
交换机类型为 Direct 时,会把消息交给符合指定 routing key 的队列。
队列和交换机的绑定,不是任意的绑定了,而是要指定一个 RoutingKey(路由 key)
消息的发送方在向 Exchange 发送消息时,也需要指定消息的 RoutingKey。
Exchange 也不再把消息交给每一个绑定的 key,而是根据消息的 RoutingKey 进行判断,只有队列的 RoutingKey 和消息的 RoutingKey 完全一致,才会接收到消息。
4.1 编写生产者代码
和发布订阅模式的区别就是:交换机类型不同,绑定队列的 RoutingKey 不同。
定义队列和交换机
java
// 路由模式
public static final String DIRECT_QUEUE1 = "direct.queue1";
public static final String DIRECT_QUEUE2 = "direct.queue2";
public static final String DIRECT_EXCHANGE = "direct.exchange";
声明队列和交换机
java
// 声明队列
@Bean("directQueue1")
public Queue directQueue1() {
return QueueBuilder.durable(Constants.DIRECT_QUEUE1).build();
}
@Bean("directQueue2")
public Queue directQueue2() {
return QueueBuilder.durable(Constants.DIRECT_QUEUE2).build();
}
// 声明交换机
@Bean("directExchange")
public DirectExchange directExchange() {
return ExchangeBuilder.directExchange(Constants.DIRECT_EXCHANGE).durable(true).build();
}
绑定队列和交换机
java
// 队列和交换机绑定
// 队列1绑定orange
@Bean("directQueueBinding1")
public Binding directQueueBinding1(@Qualifier("directExchange") DirectExchange directExchange, @Qualifier("directQueue1") Queue queue) {
return BindingBuilder.bind(queue).to(directExchange).with("orange");
}
// 队列2绑定black,orange
@Bean("directQueueBinding2")
public Binding directQueueBinding2(@Qualifier("directExchange") DirectExchange directExchange, @Qualifier("directQueue2") Queue queue) {
return BindingBuilder.bind(queue).to(directExchange).with("black");
}
@Bean("directQueueBinding3")
public Binding directQueueBinding3(@Qualifier("directExchange") DirectExchange directExchange, @Qualifier("directQueue2") Queue queue) {
return BindingBuilder.bind(queue).to(directExchange).with("orange");
}
绑定关系如下图所示:

使用接口发送消息
java
@RequestMapping("/direct/{routingKey}")
public String direct(@PathVariable("routingKey") String routingKey) { // 从路径中拿到routingKey, 需要使用PathVariable注解
// routingkey作为参数传递
rabbitTemplate.convertAndSend(Constants.DIRECT_EXCHANGE, routingKey, "hello spring amqp: direct, my routing Key is " + routingKey);
return "发送成功";
}
然后运行程序,从管理平台上可以看到,此时队列没有任何消息

然后绑定关系如下

此时,我们通过接口 http://127.0.0.1:8080/producer/direct/orange 来发送 orange 消息

然后再通过接口 http://127.0.0.1:8080/producer/direct/black 发送 balck 消息,该消息只能由队列 2 拿到

4.2 编写消费者代码
交换机和队列的绑定关系及声明已经在生产方写完,所以消费者不需要再写了。
定义监听类,处理接收到的消息即可。
java
package com.edison.rabbitmq.listener;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class DirectListener {
@RabbitListener(queues = Constants.DIRECT_QUEUE1)
public void queueListener1(String message) {
System.out.println("队列["+Constants.DIRECT_QUEUE1+"] 接收到消息: "+message);
}
@RabbitListener(queues = Constants.DIRECT_QUEUE2)
public void queueListener2(String message) {
System.out.println("队列["+Constants.DIRECT_QUEUE2+"] 接收到消息: "+message);
}
}
4.3 运行程序
运行项目
先调用接口 http://127.0.0.1:8080/producer/direct/orange 发送 routingkey 为 orange 的消息
观察后端日志,队列 1 和队列 2 收到消息

然后调用接口 http://127.0.0.1:8080/producer/direct/black 发送 routingkey 为 black 的消息
观察后端日志,队列 2 收到消息

5. 通配符模式
主题(Topics)和路由(Routing)模式的区别是:
- 主题模式使用的交换机类型为主题(topic)(路由模式使用的交换机类型为直连(direct))
- 主题类型的交换机在匹配规则上进行了扩展,绑定键(Binding Key)支持通配符匹配
5.1 编写生产者代码
和发布订阅模式的区别就是:交换机类型不同,绑定队列的路由键(RoutingKey)不同。
定义队列和交换机
java
// 通配符模式
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
public static final String TOPIC_EXCHANGE = "topic.exchange";
声明队列和交换机
java
// 声明队列
@Bean("topicQueue1")
public Queue topicQueue1() {
return QueueBuilder.durable(Constants.TOPIC_QUEUE1).build();
}
@Bean("topicQueue2")
public Queue topicQueue2() {
return QueueBuilder.durable(Constants.TOPIC_QUEUE2).build();
}
// 声明交换机
@Bean("topicExchange")
public TopicExchange topicExchange() {
return ExchangeBuilder.topicExchange(Constants.TOPIC_EXCHANGE).durable(true).build();
}
绑定队列和交换机
java
// 队列和交换机绑定
// 队列1绑定[*.orange.*]
@Bean("topicQueueBinding1")
public Binding topicQueueBinding1(@Qualifier("topicExchange") TopicExchange topicExchange, @Qualifier("topicQueue1") Queue queue) {
return BindingBuilder.bind(queue).to(topicExchange).with("*.orange.*");
}
// 队列2绑定[*.*.rabbit]
@Bean("topicQueueBinding2")
public Binding topicQueueBinding2(@Qualifier("topicExchange") TopicExchange topicExchange, @Qualifier("topicQueue2") Queue queue) {
return BindingBuilder.bind(queue).to(topicExchange).with("*.*.rabbit");
}
// 队列2绑定[lazy.#]
@Bean("topicQueueBinding3")
public Binding topicQueueBinding3(@Qualifier("topicExchange") TopicExchange topicExchange, @Qualifier("topicQueue2") Queue queue) {
return BindingBuilder.bind(queue).to(topicExchange).with("lazy.#");
}
绑定关系如下图所示:

然后使用接口发送消息
java
@RequestMapping("/topic/{routingKey}")
public String topic(@PathVariable("routingKey") String routingKey) { // 从路径中拿到routingKey, 需要使用PathVariable注解
// routingkey作为参数传递
rabbitTemplate.convertAndSend(Constants.TOPIC_EXCHANGE, routingKey, "hello spring amqp: topic, my routing Key is " + routingKey);
return "发送成功";
}
然后运行程序,从管理平台上可以看到,此时队列没有任何消息

然后绑定关系如下

5.2 编写消费者代码
定义监听类,处理接收到的消息。
java
package com.edison.rabbitmq.listener;
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class TopicListener {
@RabbitListener(queues = Constants.TOPIC_QUEUE1)
public void queueListener1(String message) {
System.out.println("队列["+Constants.TOPIC_QUEUE1+"] 接收到消息: "+message);
}
@RabbitListener(queues = Constants.TOPIC_QUEUE2)
public void queueListener2(String message) {
System.out.println("队列["+Constants.TOPIC_QUEUE2+"] 接收到消息: "+message);
}
}
5.3 运行程序
运行项目
调用接口 http://127.0.0.1:8080/producer/topic/green.orange.c 发送 routingkey 为 green.orange.c 的消息
观察后端日志,队列 1 收到消息

调用接口 http://127.0.0.1:8080/producer/topic/green.orange.rabbit 发送 routingkey 为 green.orange.rabbit 的消息
观察后端日志,队列 1 和队列 2 均收到消息

调用接口 http://127.0.0.1:8080/producer/topic/lazy.orange.rabbit 发送 routingkey 为 lazy.orange.rabbit 的消息
观察后端日志,队列 1 和队列 2 均收到消息
