MQ 从 0 到 1(二):RabbitMQ 基础模型
上一篇讲了为什么需要 MQ。
这一篇开始看 RabbitMQ。
RabbitMQ 是一个很常见的消息队列中间件,它的核心模型可以先记成一句话:
生产者不直接把消息发给队列,而是先发给交换机,交换机根据规则把消息路由到队列,消费者再从队列里消费。
这句话很重要。
很多初学者以为 RabbitMQ 是:
Producer -> Queue -> Consumer但更准确的是:
Producer -> Exchange -> Queue -> ConsumerRabbitMQ 里的几个角色
先看核心概念。
| 概念 | 作用 |
|---|---|
| Producer | 生产者,负责发送消息 |
| Exchange | 交换机,负责路由消息 |
| Queue | 队列,负责存放消息 |
| Binding | 绑定关系,把交换机和队列关联起来 |
| Routing Key | 路由键,生产者发送消息时携带 |
| Consumer | 消费者,负责处理消息 |
| Broker | RabbitMQ 服务本身 |
可以用快递系统类比:
Producer:寄件人Exchange:分拣中心Queue:派送站点Consumer:快递员Routing Key:地址信息Binding:分拣规则生产者把包裹交给分拣中心。
分拣中心根据地址和规则,把包裹送到不同站点。
快递员从站点拿包裹去派送。
为什么要有 Exchange
如果生产者直接发队列,会有一个问题:
生产者必须知道消息要发到哪个队列。
这会让生产者和队列强绑定。
RabbitMQ 加了一层 Exchange,让生产者只关心业务事件。
比如订单创建后:
订单服务发送消息到 order.exchangeroutingKey = order.created至于这条消息最终进哪个队列,由交换机和绑定规则决定。
order.exchange + order.created -> sms.queue + order.created -> point.queue + order.created -> search.queue这样新增一个消费者,只需要新增队列和绑定关系,不需要改生产者逻辑。
Direct Exchange
direct 是最直观的交换机。
它按照 routing key 精确匹配。
Exchange: order.direct
Binding:sms.queue <绑定> order.createdrefund.queue <绑定> order.refunded发送消息时:
routingKey = order.created消息会进入:
sms.queue如果发送:
routingKey = order.refunded消息会进入:
refund.queue适合场景:
- 根据明确业务动作分发;
- 一个 routing key 对应一个或多个队列;
- 比如订单创建、订单取消、退款成功。
Fanout Exchange
fanout 不看 routing key。
它会把消息广播给所有绑定到这个交换机的队列。
Exchange: order.fanout
Binding:sms.queuepoint.queuesearch.queueanalysis.queue生产者发送一条订单创建消息。
所有绑定队列都会收到。
order.fanout -> sms.queue -> point.queue -> search.queue -> analysis.queue适合场景:
- 一个事件需要多个系统都知道;
- 生产者不关心具体有哪些消费者;
- 比如订单创建广播、配置刷新广播。
Topic Exchange
topic 支持通配符匹配,是业务里非常常用的交换机。
常见规则:
* 匹配一个单词# 匹配零个或多个单词比如 routing key 设计成:
order.createdorder.paidorder.cancelledrefund.createdrefund.success绑定规则:
order.* 匹配 order.created、order.paid、order.cancelledorder.# 匹配所有 order 开头的多级事件*.created 匹配 order.created、refund.created适合场景:
- 消息类型比较多;
- 希望按业务模块或事件类型灵活订阅;
- 比如
order.*、user.*、coupon.*。
三种交换机怎么选
| 交换机 | 特点 | 适合场景 |
|---|---|---|
| direct | routing key 精确匹配 | 明确路由 |
| fanout | 广播给所有绑定队列 | 事件广播 |
| topic | 通配符匹配 | 复杂业务事件订阅 |
如果刚开始做业务系统,我更建议优先用 topic。
因为它比 direct 灵活,又不像 fanout 那样完全广播。
比如:
order.createdorder.paidorder.cancelledorder.refunded后面想订阅全部订单事件,可以绑定:
order.#只想订阅订单创建,可以绑定:
order.createdQueue 是消息真正存放的地方
Exchange 不负责长期保存消息。
消息最终要进入 Queue。
消费者是从 Queue 中取消息。
所以,队列设计也很重要。
一个常见做法是:
业务模块 + 消费目的 + queue比如:
order.sms.queueorder.point.queueorder.search.queueorder.analysis.queue不要所有业务共用一个大队列。
否则会出现几个问题:
- 消费逻辑混在一起;
- 一个消费者慢会影响其他业务;
- 失败重试不好区分;
- 监控和排查都不清晰。
更推荐按消费目的拆队列。
Binding 是路由规则
Binding 可以理解成交换机和队列之间的路由规则。
比如:
Exchange: order.topicQueue: order.sms.queueBinding Key: order.created意思是:
当消息发送到
order.topic,并且 routing key 匹配order.created时,把消息投递到order.sms.queue。
如果有多个队列绑定同一个 routing key,那么多个队列都会收到消息。
这就是一条消息驱动多个消费者的基础。
Consumer 消费消息
消费者监听队列。
order.sms.queue -> SmsConsumerorder.point.queue -> PointConsumerorder.search.queue -> SearchConsumer每个消费者只处理自己的业务。
例如短信消费者只关心发短信:
public void handle(OrderCreatedMessage message) { smsService.sendOrderCreatedMessage(message.getOrderId());}积分消费者只关心加积分:
public void handle(OrderCreatedMessage message) { pointService.addOrderPoint(message.getOrderId());}这样业务边界会清楚很多。
消息确认 Ack
消费者收到消息后,RabbitMQ 需要知道这条消息是否已经处理成功。
这就涉及 Ack。
常见模式有两种:
| 模式 | 说明 |
|---|---|
| 自动确认 | 消息投递给消费者后就认为成功 |
| 手动确认 | 消费者处理成功后主动确认 |
真实业务里,更推荐手动确认。
因为自动确认有风险:
RabbitMQ 把消息发给消费者消费者刚收到,业务还没处理应用宕机RabbitMQ 已经认为消息成功消息丢失手动确认的思路是:
收到消息执行业务业务成功发送 ack如果业务失败,可以重试、拒绝或进入死信队列。
可靠消费会在后面的文章单独讲。
持久化
RabbitMQ 里要注意三个持久化:
- Exchange 持久化;
- Queue 持久化;
- Message 持久化。
只持久化队列还不够,消息本身也要设置为持久化。
可以粗略理解成:
Exchange 和 Queue 持久化:RabbitMQ 重启后结构还在。Message 持久化:RabbitMQ 重启后消息尽量不丢。为什么说“尽量”?
因为消息可靠性还和生产者确认、磁盘刷盘、集群配置有关。不能只靠一个 durable 就以为万无一失。
一个订单消息模型
下面是一个适合入门项目的设计。
Exchange:order.topic.exchange
Routing Key:order.createdorder.paidorder.cancelled
Queues:order.sms.queue 绑定 order.createdorder.point.queue 绑定 order.paidorder.search.queue 绑定 order.#order.analysis.queue 绑定 order.#效果:
订单创建:order.created -> 短信、搜索、分析
订单支付:order.paid -> 积分、搜索、分析
订单取消:order.cancelled -> 搜索、分析这种设计比所有消息塞进一个队列更清晰。
小结
这一篇先把 RabbitMQ 的基础模型记住:
- 生产者把消息发给 Exchange;
- Exchange 根据 Binding 和 Routing Key 把消息路由到 Queue;
- Consumer 从 Queue 消费消息;
direct适合精确路由;fanout适合广播;topic适合灵活订阅;- 业务队列建议按消费目的拆分;
- 真实项目要关注 ack 和持久化。
If this article helped you, please share it with others!
Some information may be outdated






