RabbitMQ
MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列。也就是异步调用中的Broker。
📌技术对比
| 🚀 特性 | 🐇 RabbitMQ | 🔺 ActiveMQ | 🚀 RocketMQ | 🖧 Kafka |
|---|---|---|---|---|
| 🏢 公司/社区 | Rabbit | Apache | 阿里 | Apache |
| 💻 开发语言 | Erlang | Java | Java | Scala & Java |
| 📜 协议支持 | AMQP, XMPP, SMTP, STOMP | OpenWire, STOMP, REST, XMPP, AMQP | 自定义协议 | 自定义协议 |
| ✅ 可用性 | 高 | 一般 | 高 | 高 |
| 📊 单机吞吐量 | 一般 | 差 | 高 | 非常高 |
| ⏱ 消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒以内 |
| 🔒 消息可靠性 | 高 | 一般 | 高 | 一般 |
RabbitMQ的整体架构及核心概念:
-
virtual-host:虚拟主机,起到数据隔离的作用
-
publisher: 消息发送者
-
consumer:消息的消费者
-
queue:队列,存储消息
-
exchange:交换机,负责路由消息

🏷Java客户端
📘快速入门
- 引入spring-amqp依赖
在父工程中引入spring-amqp依赖,这样publisher和consumer服务都可以使用:
xml
<!-- AMQP依赖,包含RabbitMQ -- >
<dependency>
<groupId>org. springframework. bootk/groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 配置RabbitMQ服务端信息
在每个微服务中引入MQ服务端信息,这样微服务才能连接到RabbitMQ
xml
spring:
rabbitmq:
host: 192.168.228.129 #主机
port: 5672 #端口
username: dengwuguang #账号
password: 1234 #密码
virtual-host: /dwg #虚拟主机
- 发送消息
SpringAMQP提供了RabbitTemplate工具类,方便我们发送消息。发送消息代码如下:
java
@SpringBootTest
public class SpringAMQPTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendMessage() {
//指定队列
String queueName = "simple.queue";
// 1.指定消息内容
String message = "Hello World !";
// 2.发送消息
rabbitTemplate.convertAndSend(queueName, message);
}
}
- 接收消息
SpringAMQP提供声明式的消息监听,我们只需要通过注解在方法上声明要监听的队列名称,将来
SpringAMQP就会把消息传递给当前方法:
java
@Slf4j
@Component
public class Mqlistener {
@RabbitListener(queues = "simple.queue")// 监听simple.queue队列
public void listenSimplequeue(String message){
System.out.println("消费者接收到的消息是:"+message);
}
}
SpringAMQP如何收发消息?
-
引入spring-boot-starter-amqp依赖
-
配置rabbitmq服务端信息
-
利用RabbitTemplate发送消息
-
利用@RabbitListener注解声明要监听的队列,监听消息
📘Work Queues
📌消费者消息推送限制
默认情况下,RabbitMQ的会将消息依次轮询投递给绑定在队列上的每一个消费者。但这并没有考虑到消费者是否已经
处理完消息,可能出现消息堆积。
因此我们需要修改application.yml,设置preFetch值为1,确保同一时刻最多投递给消费者1条消息:
xml
spring:
rabbitmq:
listener:
simple:
prefetch:1 # 每次只能获取一条消息,处理完成才能获取下一个消息
Work模型的使用:
-
多个消费者绑定到一个队列,可以加快消息处理速度
-
同一条消息只会被一个消费者处理
-
通过设置prefetch来控制消费者预取的消息数量,处理完一条再处理下一条,实现能者多劳
📘Fanout交换机
Fanout Exchange会将接收到的消息广播到每一个跟其绑定的queue,所以也叫广播模式

📌步骤
-
编写消息监听器,监听fanout.queue1和fanout.queue2
- 在consumer服务的SpringRabbitListener类中,添加两个方法,分别监听fanout.queue1和fanout.queue2:
java@RabbitListener(queues = "fanout.queue1")// 监听simple.queue队列 public void listenFanoutqueue1(String message) { System.out.println("消费者listenFanoutqueue1接收到的消息是:" + message); } @RabbitListener(queues = "fanout.queue2")// 监听simple.queue队列 public void listenFanoutqueue2(String message) { System.out.println("消费者listenFanoutqueue2接收到的消息是:" + message); } -
编写单元测试,向dwg.fanout交换机发送消息
- 在publisher服务的SpringAmqpTest类中添加测试方法:
java@Test public void testSendFanout() { //指定队列 String ExchangeName = "dwg.fanout"; // 1.指定消息内容 String message = "Hello everyone !"; // 2.发送消息 rabbitTemplate.convertAndSend(ExchangeName, null,message); }
交换机的作用是什么?
-
接收publisher发送的消息
-
将消息按照规则路由到与之绑定的队列
-
FanoutExchange的会将消息路由到每个绑定的队列
📘Direct交换机
Direct Exchange 会将接收到的消息根据规则路由到指定的Queue,因此称为定向路由。
- 每一个Queue都与Exchange设置一个BindingKey
- 发布者发送消息时,指定消息的RoutingKey
- Exchange将消息路由到BindingKey与消息RoutingKey一致的队列

步骤:
-
编写消息监听器,监听direct.queue1和direct.queue2
- 在consumer服务的SpringRabbitListener类中,添加两个方法,分别监听direct.queue1和direct.queue2:
java@RabbitListener(queues = "direct.queue1")// 监听simple.queue队列 public void listenDirectqueue1(String message) { System.out.println("消费者listenDirectqueue1接收到的消息是:" + message); } @RabbitListener(queues = "direct.queue2")// 监听simple.queue队列 public void listenDirectqueue2(String message) { System.out.println("消费者listenDirectqueue2接收到的消息是:" + message); }- 编写单元测试,向dwg.direct交换机发送消息
- 在publisher服务的SpringAmqpTest类中添加测试方法:
java@Test public void testSendDirect() { //指定队列 String ExchangeName = "dwg.direct"; // 1.指定消息内容 String message = "Hello !"; // 2.发送消息 rabbitTemplate.convertAndSend(ExchangeName, "blue",message); }
📘Topic交换机
TopicExchange与DirectExchange类似,区别在于routingKey可以是多个单词的列表,并且以.分割。
Queue与Exchange指定BindingKey时可以使用通配符:
-
#:代指0个或多个单词
-
*: 代指一个单词

描述下Direct交换机与Topic交换机的差异?
-
Topic交换机接收的消息RoutingKey可以是多个单词,以.分割
-
Topic交换机与队列绑定时的bindingKey可以指定通配符
-
#:代表0个或多个词
-
*: 代表1个词
-
📘声明队列和交换机
SpringAMQP提供了几个类,用来声明队列、交换机及其绑定关系:
-
Queue:用于声明队列,可以用工厂类QueueBuilder构建
-
Exchange:用于声明交换机,可以用工厂类ExchangeBuilder构建
-
Binding:用于声明队列和交换机的绑定关系,可以用工厂类BindingBuilder构建
方式1:
java
@Configuration
public class FanoutConfig {
@Bean
public FanoutExchange fanoutExchange(){
//声明交换机
return new FanoutExchange("dwg.fanout1");
}
@Bean
public Queue Queue(){
//声明队列
return new Queue("fanout.queue3");
}
@Bean
public Binding fanoutBinding(){
//绑定关系
return BindingBuilder.bind(Queue()).to(fanoutExchange());
}
}
SpringAMQP还提供了基于@RabbitListener注解来声明队列和交换机的方式:
方式2:
java
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red", "blue"}
)
public void listenDirectQueue1(String msg) {
System.out.println("消费者1接收到Direct消息:【"+msg+"】");
}
📘消息转换器
建议采用JSON序列化代替默认的JDK序列化,要做两件事情:
在publisher和consumer中都要引入jackson依赖:
xml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
在publisher和consumer中都要配置MessageConverter:
java
@Bean
public MessageConverter jacksonMessageConvertor(){
return new Jackson2JsonMessageConverter();
}
🧾总结:今天学习了RabbitMQ的基础部分,因为之前有使用基于stream的消息队列的经验,所以学起来比较轻松,明天学完RabbitMQ的高级部分,加油!🔥🔥🔥