解决什么问题

一、解耦

服务 A 需要通知服务 B 做某件事。如果直接调用,A 就必须知道 B 的地址、接口、可用性。B 挂了,A 的调用就失败。

引入消息队列后,A 只需把消息投递到队列,不关心谁来处理。B 从队列中取消息处理。A 和 B 只在"消息格式"上耦合。

二、削峰填谷

流量有波峰波谷。如果按峰值配置处理能力,平时大量资源闲置;如果按平均配置,峰值时会被打垮。

消息队列作为缓冲:峰值时请求先入队,消费者按自己的处理能力慢慢消费。

这是"用时间换资源"的典型思路。

三、异步

有些操作不需要立即完成(发送通知、生成报表、更新搜索索引)。把它们放入队列异步处理,可以让主流程更快返回,改善用户体验。

两种模型

点对点(Queue)

一条消息只被一个消费者处理。多个消费者竞争消费,实现负载均衡。

适合任务分发。

发布/订阅(Topic)

一条消息被所有订阅者接收。

适合事件广播——比如"订单已创建"这个事件,库存服务、积分服务、通知服务都需要知道。

交付语义

这是设计时必须明确的:

最多一次(At most once):消息可能丢失,但不会重复。适合对可靠性要求不高的场景(如日志收集)。

至少一次(At least once):消息不会丢失,但可能重复。这是最常见的默认行为——消费者处理完后发送确认(ACK),如果确认前崩溃,消息会被重新投递。

恰好一次(Exactly once):不丢不重。实现难度最高,通常需要幂等性配合事务机制。有些系统的"精确一次"是通过"至少一次 + 幂等消费"来实现的。

幂等性

由于"至少一次"会导致重复投递,消费者必须设计成幂等的——同一条消息处理多次,结果应该与处理一次相同。

常见做法:

  • 为每条消息分配唯一 ID,消费者记录已处理的 ID;
  • 用数据库的唯一约束去重;
  • 设计操作时使用"设置"而非"递增"语义。

常见问题

消息堆积:生产速度超过消费速度。解决方法是扩容消费者、优化消费逻辑,或者设置告警与降级策略。

消息丢失:可能发生在生产端(未确认就返回)、队列端(未持久化就宕机)、消费端(自动确认但处理失败)。要针对性地配置持久化和确认机制。

顺序性:多数队列只保证分区内有序,不保证全局有序。如果业务要求严格顺序,需要把所有相关消息发到同一个分区,并在消费者端做串行处理。

死信队列(DLQ):反复处理失败的消息,不应无限重试(会阻塞队列)。通常设置最大重试次数,超过后转入死信队列,供人工排查。

选型考虑

  • 吞吐量:每秒能处理多少消息;
  • 延迟:从生产到消费的时间;
  • 持久化与可靠性:是否支持持久化、副本;
  • 功能:事务消息、延迟消息、消息回溯;
  • 运维复杂度:集群搭建、监控、扩容。

说明:本文为分布式系统组件的公开知识整理。