跳转到内容

顺序消息

用途:确实需要“同一实体的事件按序处理”时正确实现,并理解代价

类型 含义 可行性
全局顺序 Topic 内所有消息按发送顺序消费 只能单队列单消费者,吞吐极低,几乎不该用
分区顺序 同一业务键(订单、账户)的消息有序 标准做法:按 key 路由到固定队列
  1. 生产端:用 MessageQueueSelector 以业务键哈希选择队列,保证同一实体的消息进同一队列。
    • 队列数变更(扩容)会改变映射,历史消息与新消息可能落到不同队列 → 迁移期需要双读或冻结路由。
  2. Broker 端:队列内 FIFO(RocketMQ 天然满足,前提是消息未被重试打乱)。
  3. 消费端:该队列必须单线程串行消费(顺序监听器会锁住队列),且失败时不能并发继续。

顺序消费下重试会阻塞该队列后续消息(避免乱序),因此:

  • 失败必须快速判定“可重试”还是“必须跳过”;不可恢复的错误应进入死信/人工处理,而不是无限阻塞队列。
  • 重试次数与间隔要设上限,并配告警:队列被卡住是延迟事故的前兆。

问一句:“乱序真的会造成业务错误吗?”

场景 是否真需要顺序 更合适的方案
状态机推进(创建→支付→发货) 需要,但可用状态版本号兜底 消息携带版本号/状态,消费端拒绝旧版本
库存增量变更 需要 改为传“最终值”而非“增量”(幂等且乱序安全)
用户操作日志展示 不需要严格顺序 按业务时间戳排序展示
数据同步(binlog) 需要 CDC 工具按分区键有序投递

优先用“消息自带版本/最终状态”消除顺序依赖,比维持顺序链路成本低得多。

顺序消费把并行度降到队列粒度:单队列吞吐受限于单线程处理速度。要提升吞吐只能增加队列数(同时保证 key 分布均匀),而热点实体(大客户订单)无法通过加队列解决——此时应重新设计消息内容(幂等 + 版本)而不是加顺序。

误区 修正
以为“同一个 Topic”就有序 只有队列内有序
顺序消费里做长耗时操作 队列被卡,延迟快速累积
失败无限重试保序 阻塞后续消息,需上限 + 死信
扩容队列数后认为顺序仍成立 路由映射变化,需迁移策略
用全局顺序解决设计问题 吞吐归零,应改消息语义
  • Apache RocketMQ 官方文档:Ordered Messages
  • 各 CDC 工具(Canal/Debezium)关于分区内有序的说明