Spring AMQP 三大交换机详解:Fanout、Direct、Topic(附完整示例 + 使用场景)
在使用 Spring Boot + RabbitMQ 做消息队列开发时,经常会遇到三种交换机类型:
- FanoutExchange
- DirectExchange
- TopicExchange
很多刚接触 MQ 的同学都会有几个疑问:
- 这三种交换机到底有什么区别?
- routingKey 是干什么的?
- 实际项目应该选哪一种?
这篇文章会用 通俗解释 + 简单代码 + 实际场景,把三种交换机彻底讲清楚。
为了让重点更清晰,本文 只写核心代码,不写配置文件。
一、RabbitMQ 的核心结构
很多人刚开始会以为消息队列是这样:
生产者 → 队列 → 消费者
其实 RabbitMQ 并不是这样工作的。
真正的结构是:
Producer → Exchange → Queue → Consumer
也就是说:
生产者永远只把消息发送到 Exchange(交换机)。
然后 Exchange 再根据规则,把消息分发到不同队列。
不同类型的 Exchange,决定了:
消息会被分发到哪些队列。
二、FanoutExchange(广播模式)
1、工作原理
FanoutExchange 是最简单的一种交换机。
它的规则只有一句话:
不看 routingKey,直接把消息广播到所有绑定的队列。
结构如下:
FanoutExchange
/ | \
Q1 Q2 Q3
只要有消息进入交换机:
Producer → Exchange → 所有队列都会收到
2、声明持久化队列和交换机
java
@Bean
public Queue queue1(){
return new Queue("queue1", true);
}
@Bean
public Queue queue2(){
return new Queue("queue2", true);
}
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanout.exchange");
}
@Bean
public Binding binding1(){
return BindingBuilder.bind(queue1()).to(fanoutExchange());
}
@Bean
public Binding binding2(){
return BindingBuilder.bind(queue2()).to(fanoutExchange());
}
这里的 true 表示 持久化队列,RabbitMQ 重启后不会丢失。
3、发送消息
java
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(){
rabbitTemplate.convertAndSend(
"fanout.exchange",
"",
"系统广播消息"
);
}
注意:
FanoutExchange 不需要 routingKey
所以直接写空字符串。
4、消费者
java
@RabbitListener(queues = "queue1")
public void receive1(String msg){
System.out.println("queue1收到:" + msg);
}
@RabbitListener(queues = "queue2")
public void receive2(String msg){
System.out.println("queue2收到:" + msg);
}
5、适用场景
Fanout 非常适合:
- 系统广播
- 缓存同步
- 日志收集
- 事件通知
例如:
用户注册成功
需要:
- 发优惠券
- 发短信
- 写日志
就可以:
user-service → fanoutExchange
多个服务同时消费。
6、优缺点
优点:
- 结构简单
- 扩展容易
缺点:
- 所有队列都会收到消息
- 可能产生无用消息
三、DirectExchange(精准路由)
1、工作原理
DirectExchange 根据 routingKey 精确匹配。
结构:
DirectExchange
/ \
order.queue log.queue
(order) (log)
规则:
routingKey = order → order.queue
routingKey = log → log.queue
2、声明交换机
java
@Bean
public DirectExchange directExchange(){
return new DirectExchange("direct.exchange");
}
3、绑定 routingKey
java
@Bean
public Binding orderBinding(){
return BindingBuilder
.bind(new Queue("order.queue",true))
.to(directExchange())
.with("order");
}
4、发送消息
java
rabbitTemplate.convertAndSend(
"direct.exchange",
"order",
"订单创建成功"
);
只有绑定了 order 的队列才会收到消息。
5、适用场景
DirectExchange 常用于:
- 订单系统
- 日志系统
- 业务分类处理
例如:
order.create
order.pay
order.cancel
不同服务处理不同消息。
6、优缺点
优点:
- 路由精准
- 性能高
缺点:
- routingKey 必须完全匹配
四、TopicExchange(通配符路由)
1、工作原理
TopicExchange 是 最灵活的交换机。
它支持 通配符匹配。
两个常见符号:
* 匹配一个单词
# 匹配多个单词
例如:
order.create
order.pay
order.cancel
结构:
TopicExchange
/ \
order.# order.create
规则:
order.# → 所有订单消息
order.create → 只接收创建订单
2、声明交换机
java
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topic.exchange");
}
3、绑定规则
java
@Bean
public Binding topicBinding(){
return BindingBuilder
.bind(new Queue("order.queue",true))
.to(topicExchange())
.with("order.#");
}
4、发送消息
java
rabbitTemplate.convertAndSend(
"topic.exchange",
"order.create",
"创建订单"
);
5、适用场景
TopicExchange 常见于:
- 微服务架构
- 事件驱动系统
- 大型业务系统
例如:
user.login
user.logout
user.register
不同模块订阅不同事件。
6、优缺点
优点:
- 非常灵活
- 支持复杂路由
缺点:
- routingKey 设计必须规范
五、三种交换机对比
| 类型 | routingKey | 特点 | 场景 |
|---|---|---|---|
| Fanout | 不需要 | 广播 | 系统通知 |
| Direct | 精确匹配 | 精准路由 | 订单系统 |
| Topic | 通配符 | 灵活 | 微服务 |
简单记忆:
Fanout = 广播
Direct = 精确路由
Topic = 模糊路由
六、真实项目案例
假设有一个 用户注册系统。
用户注册成功后,需要做三件事:
发优惠券
发短信
写日志
可以这样设计:
UserService
↓
FanoutExchange
/ | \
优惠券服务 短信服务 日志服务
这样一条消息就可以同时通知多个系统。
七、面试常见问题
1 RabbitMQ 为什么需要 Exchange?
因为 Exchange 可以 解耦生产者和队列。
生产者只需要发送消息,不需要关心具体队列。
2 Fanout 和 Direct 的区别
Fanout:
广播
Direct:
精准路由
3 为什么 Topic 使用最多?
因为 Topic 既可以实现:
- Direct 的精准路由
- Fanout 的广播
所以 灵活性最高。
八、总结
三种交换机可以这样理解:
- Fanout:一条消息,所有人都听见
- Direct:一条消息,只给指定的人
- Topic:一条消息,按规则筛选接收人
掌握这三种交换机之后,你就已经理解了 RabbitMQ 消息路由的核心机制。
如果这篇文章对你有帮助,欢迎点赞收藏 ⭐
后面我还会继续写:
- Spring AMQP 实战
- RabbitMQ 延迟队列
- RabbitMQ 死信队列