【Java 开发日记】我们来说一下消息的可靠性投递
在实际生产环境中,“消息不丢、不重、不乱序” 是绝大多数中间件使用者的核心诉求,但真正能同时满足这三点的系统非常少。
我们今天把主流消息中间件在可靠性投递这个维度上做一次比较清晰的盘点和实践总结(2025-2026 主流方案视角)。
一、可靠性投递的三个阶段 & 六个关键点
| 阶段 | 核心问题 | 业界常见解决方案 | 丢失风险点 | 重复风险点 |
|---|---|---|---|---|
| 生产者 → Broker | 生产者发送成功了吗?网络抖动/宕机? | 同步发送 + 重试 + 回调 / 事务消息 / 发送确认 | ★★★★ | ★★ |
| Broker 内部 | Broker 收到后是否持久化?单点/主从? | 同步刷盘 / 异步刷盘 + 多副本(ISR) | ★★★ | ★ |
| Broker → 消费者 | 消费者真正消费成功了吗?重复消费? | 手动提交 + 幂等 + 至少一次/至多一次/恰好一次 | ★★ | ★★★★★ |
一句话总结目前真实世界的取舍:
大多数公司最终接受的真相是:
“宁可重复消费,也不要丢失消息”
→ 所以目前生产上 99.9% 的 Java 业务系统最终选择的都是:至少一次投递 + 业务幂等
二、2025-2026 主流中间件可靠性能力对比
| 中间件 | 生产者确认机制 | Broker 持久化方式 | 消费确认方式 | 是否原生支持恰好一次 | 生产环境最常见用法 | 可靠性难度 | 维护成本 |
|---|---|---|---|---|---|---|---|
| Kafka | acks=all + retries | 刷盘策略 + 多副本 ISR | 手动 commit + 幂等 | 部分支持(事务+幂等) | 至少一次 + 幂等 | ★★ | ★★ |
| RocketMQ 5.x | 同步/异步 + 发送回调 | 同步双写/异步刷盘 | 手动 ACK + 幂等 | 支持事务消息 | 事务消息 / 普通消息+幂等 | ★★☆ | ★★★ |
| Pulsar | 生产者确认 + ledger | Bookie 多副本 + 强一致写 | 消息游标 + 手动/自动 | 原生支持(Exactly-once) | Exactly-once(函数计算/流计算多用) | ★★★ | ★★★★ |
| RabbitMQ | Publisher Confirms | 持久化队列+持久化消息 | 手动 ACK | 不支持(需幂等) | 至少一次 + 幂等 | ★★ | ★★★★ |
| Redis Stream | XADD + ACK | AOF + 主从/哨兵/集群 | XACK + Pending 机制 | 需要业务幂等 | 小规模/低延迟场景 | ★★★★ | ★★ |
三、生产中最常用的几种“可靠性投递”组合方案(推荐度排序)
方案A:最常用、最稳(90%+公司都在用)
Kafka/RocketMQ + 至少一次 + 业务强幂等
难度:★☆
可靠性:★★★★★
维护性:★★★★★
代表公司:几乎所有中大型互联网公司
方案B:事务消息路线(金融、对账、资金相关强诉求)
RocketMQ 事务消息 / Kafka 事务
难度:★★★
可靠性:★★★★★
维护性:★★★
代表场景:订单创建、余额扣减、积分发放等
方案C:追求极致恰好一次(流计算、下游是数据库/搜索引擎)
Pulsar Exactly-Once / Kafka + 事务 + 幂等 + 幂等检查表
难度:★★★★
可靠性:★★★★★
维护性:★★
代表场景:实时数仓、实时索引构建、CDC 场景
方案D:经典但正在被逐渐抛弃的方案
RabbitMQ 持久化队列 + 手动 ACK + 死信队列兜底
→ 现在很多团队在逐步迁移到 Kafka 或 RocketMQ
四、2025-2026 真实项目中最推荐的“生产保底组合拳”(建议收藏)
1. 生产者端:
- 开启发送确认(acks=all / confirm)
- 开启合理重试(3~5次 + 指数退避)
- 记录发送日志(业务id+消息id+时间+状态)
2. Broker端:
- 至少 3 副本(min.insync.replicas=2 或同步双写)
- 重要消息开启同步刷盘(flush模式)
3. 消费端(最关键!):
- 必须手动提交(disable auto commit)
- 消费成功后再 commit
- 消费逻辑必须严格幂等(推荐方式排序):
1. 数据库唯一索引/唯一约束
2. 业务防重表(dedup表)
3. redis setnx / 布隆过滤器
4. 分布式锁 + 查询状态
4. 监控与补偿(兜底):
- 监控消息堆积、消费延迟、重复率
- 建立死信队列/重试队列
- 定时扫描“疑似漏单”进行补偿对账
小结口诀(背下来就基本不会翻车)
发消息:确认 + 重试 + 记录
存消息:多副本 + 同步刷(关键业务)
收消息:手动 ack + 强幂等
兜底:死信 + 监控 + 对账补偿
绝大多数场景下做到上面这四句话,
消息丢失概率可以做到 10个9 以下,
而重复消费基本都能被幂等消化掉。
需要我针对某个具体中间件(Kafka/RocketMQ/Pulsar)
再展开写一套最推荐的生产配置 + 代码模板吗?
直接告诉我你现在用的中间件和业务场景~