现场模拟:上周三,凌晨1点。监控警报刺耳地响着:订单系统消息堆积1000+,用户疯狂投诉”下单失败”。打开代码一看,心都凉了——我们用错了交换机类型。
“为什么用Fanout交换机处理订单消息?”技术总监的声音在电话里发抖。这不是代码写得差,是对交换机类型理解不清的典型翻车现场。
别担心,今天不讲理论,只讲实战:RabbitMQ的四种交换机类型,以及为什么我们最终选择了Topic交换机。
RabbitMQ的四种交换机:不是所有都一样
1. Direct交换机:精确匹配(像快递柜)
// 订单系统用Direct
rabbitTemplate.convertAndSend("order_exchange", "order.created", order);
特点:
- 消息发送到指定路由键(routing key)
- 例如:
order.created→ 只发给绑定order.created的队列
适用场景:
- 订单创建、库存扣减等精准业务
- 一个消息只给一个消费者
2. Topic交换机:模糊匹配(像搜索引擎)
// 订单系统用Topic
rabbitTemplate.convertAndSend("order_exchange", "order.created", order);
特点:
- 支持通配符
*(单级)和#(多级) - 例如:
order.*→ 匹配order.created、order.updated
适用场景:
- 我们项目选择的:订单、库存、日志等需要灵活匹配的场景
- 一个消息可以同时发给多个消费者
3. Fanout交换机:广播(像群发短信)
// 通知系统用Fanout
rabbitTemplate.convertAndSend("notification_exchange", "", notification);
特点:
- 忽略路由键,所有绑定的队列都收到消息
- 例如:发送到
notification_exchange,所有队列都收到
适用场景:
- 系统通知、日志广播等全量推送
- 但不是我们订单系统的首选
4. Headers交换机:Header匹配(几乎不用)
// 一般不推荐使用
MessageProperties props = new MessageProperties();
props.setHeader("type", "order");
Message message = new Message(orderJson.getBytes(), props);
rabbitTemplate.convertAndSend("header_exchange", "", message);
特点:
- 通过消息Header匹配,不是路由键
- 配置复杂,性能差
适用场景:
- 极少数特殊场景(我们项目没用过)
为什么我们最终选了Topic交换机?——血泪教训
误区:一开始用Fanout处理订单消息
// 错误写法:用Fanout广播所有消息
rabbitTemplate.convertAndSend("order_exchange", "", order);
问题:
- 订单消息发给所有队列(包括日志队列、通知队列)
- 日志队列处理订单消息 → 业务混乱
- 10万并发时,日志队列堆积1000+消息,系统卡死
正确方案:用Topic交换机精准匹配
// 正确写法:Topic交换机
rabbitTemplate.convertAndSend(
"order_exchange",
"order.created",
order
);
为什么选Topic?
- 精准解耦:
- 订单创建消息只发给库存服务,不发给日志服务
- 避免了消息污染(日志队列不用处理订单)
- 灵活扩展:
- 未来新增”优惠券”服务,只需绑定
order.* - 无需修改订单系统代码
- 未来新增”优惠券”服务,只需绑定
- 性能优势:
- 消息只发给需要的队列,减少无效处理
- 10万QPS时,系统响应时间从1.5s→0.2s
实战配置:三行代码搞定
1. 创建Topic交换机(Spring Boot)
@Bean
public Exchange orderExchange() {
return new TopicExchange("order_exchange");
}
2. 创建队列并绑定(Spring Boot)
@Bean
public Queue inventoryQueue() {
return new Queue("inventory_queue");
}
@Bean
public Binding inventoryBinding() {
return BindingBuilder.bind(inventoryQueue())
.to(orderExchange())
.with("order.created"); // 绑定路由键
}
3. 发送消息(订单服务)
rabbitTemplate.convertAndSend(
"order_exchange",
"order.created",
order
);
避坑指南:交换机选型常见错误
| 错误 | 现象 | 解决方案 |
|---|---|---|
| 用Fanout处理订单 | 日志队列处理订单消息 | 改用Topic,绑定精确路由键 |
| 用Direct处理多业务 | 无法扩展新服务 | 改用Topic,用order.*匹配 |
| 混用交换机类型 | 消息流向混乱 | 统一规范:核心业务用Topic |
真实案例:某电商曾用Fanout处理订单,结果日志系统每秒处理10万订单消息,CPU 95%。改用Topic后,日志系统负载从10万→500,系统稳定如狗。
为什么不是用Direct?——我们的经验
Direct交换机:
- 适合”一对一”场景(如用户登录通知)
- 但不适合订单系统:
- 订单消息需要同时给库存、日志、优惠券服务
- 用Direct需要创建多个交换机,代码臃肿
Topic交换机:
- 用
order.*匹配所有订单事件 - 1个交换机搞定所有业务,代码简洁
- 扩展新服务只需加绑定,不改代码
结语:交换机选型不是玄学,是业务的精准匹配
RabbitMQ的交换机不是”随便选一个就行”,而是业务场景的精准匹配。
- 订单系统 → Topic(精准+灵活)
- 通知系统 → Fanout(广播)
- 用户登录 → Direct(一对一)
上周的崩溃告警,现在再看只是个教训。用对交换机,系统从”卡顿”到”流畅”,从”崩溃”到”稳定”。
记住:
- 不要为了用交换机而用交换机
- 业务需要什么,就用什么交换机
- 选对了,系统稳定;选错了,半夜爬起来修服务器