顺序消息
用途:确实需要“同一实体的事件按序处理”时正确实现,并理解代价
两种顺序
「两种顺序」小节链接| 类型 | 含义 | 可行性 |
|---|---|---|
| 全局顺序 | Topic 内所有消息按发送顺序消费 | 只能单队列单消费者,吞吐极低,几乎不该用 |
| 分区顺序 | 同一业务键(订单、账户)的消息有序 | 标准做法:按 key 路由到固定队列 |
实现三要素
「实现三要素」小节链接- 生产端:用
MessageQueueSelector以业务键哈希选择队列,保证同一实体的消息进同一队列。- 队列数变更(扩容)会改变映射,历史消息与新消息可能落到不同队列 → 迁移期需要双读或冻结路由。
- Broker 端:队列内 FIFO(RocketMQ 天然满足,前提是消息未被重试打乱)。
- 消费端:该队列必须单线程串行消费(顺序监听器会锁住队列),且失败时不能并发继续。
重试与顺序的冲突
「重试与顺序的冲突」小节链接顺序消费下重试会阻塞该队列后续消息(避免乱序),因此:
- 失败必须快速判定“可重试”还是“必须跳过”;不可恢复的错误应进入死信/人工处理,而不是无限阻塞队列。
- 重试次数与间隔要设上限,并配告警:队列被卡住是延迟事故的前兆。
常见需求辨析
「常见需求辨析」小节链接问一句:“乱序真的会造成业务错误吗?”
| 场景 | 是否真需要顺序 | 更合适的方案 |
|---|---|---|
| 状态机推进(创建→支付→发货) | 需要,但可用状态版本号兜底 | 消息携带版本号/状态,消费端拒绝旧版本 |
| 库存增量变更 | 需要 | 改为传“最终值”而非“增量”(幂等且乱序安全) |
| 用户操作日志展示 | 不需要严格顺序 | 按业务时间戳排序展示 |
| 数据同步(binlog) | 需要 | CDC 工具按分区键有序投递 |
优先用“消息自带版本/最终状态”消除顺序依赖,比维持顺序链路成本低得多。
性能代价
「性能代价」小节链接顺序消费把并行度降到队列粒度:单队列吞吐受限于单线程处理速度。要提升吞吐只能增加队列数(同时保证 key 分布均匀),而热点实体(大客户订单)无法通过加队列解决——此时应重新设计消息内容(幂等 + 版本)而不是加顺序。
常见误区
「常见误区」小节链接| 误区 | 修正 |
|---|---|
| 以为“同一个 Topic”就有序 | 只有队列内有序 |
| 顺序消费里做长耗时操作 | 队列被卡,延迟快速累积 |
| 失败无限重试保序 | 阻塞后续消息,需上限 + 死信 |
| 扩容队列数后认为顺序仍成立 | 路由映射变化,需迁移策略 |
| 用全局顺序解决设计问题 | 吞吐归零,应改消息语义 |
延伸阅读
「延伸阅读」小节链接- Apache RocketMQ 官方文档:Ordered Messages
- 各 CDC 工具(Canal/Debezium)关于分区内有序的说明