分布式消息投递模型快速上手

文章目录

在分布式消息系统中, 投递模型 定义了消息从生产者发送到消费者接收的可靠性保证。其中 "至少一次投递" 是最常用、也是理解其他模型的基础。

1.三种投递模型

模型 英文 含义 可能情况
至多一次 At Most Once 消息可能丢失,但绝不会重复 丢消息 ✅ / 不丢 ❌ / 重复 ❌
至少一次 At Least Once 消息不会丢失,但可能重复 丢消息 ❌ / 不丢 ✅ / 重复 ✅
恰好一次 Exactly Once 消息既不丢失也不重复 丢消息 ❌ / 不丢 ✅ / 重复 ❌

在分布式系统中,Exactly Once 通常是由"至少一次 + 幂等消费"组合实现的理想状态,而不是单一机制能保证的。

2.至少一次投递详解

2.1 工作原理

消费者 消息队列 生产者 消费者 消息队列 生产者 如果消费者 ACK 丢失(网络超时等) 发送消息 M 返回 ACK (确认收到) 拉取消息 返回消息 M 处理完成,返回 ACK 标记消息已消费 重新投递消息 M (可能重复)

核心机制

  • 生产者端:消息队列确认收到后才算成功,否则重发
  • 消费者端:必须主动发送 ACK 确认,队列才认为消费完成
  • 重试机制:如果超时或收到错误,队列会重新投递

2.2 优缺点

优点 缺点
✅ 消息不会丢失(可靠性高) ❌ 可能收到重复消息
✅ 实现相对简单 ❌ 消费者必须实现幂等性
✅ 大多数消息队列默认支持 ❌ 重复消息可能影响业务逻辑

2.3 典型应用场景

场景 原因
订单支付通知 支付成功消息绝对不能丢,重复可通过业务去重
日志采集 宁可重复采集,也不能丢失重要日志
数据同步 数据可以重复同步(通过版本号去重)
异步任务 任务可以重复执行(需幂等设计)

3.三种模型的实现原理

3.1 至多一次 (At Most Once)

发送消息
直接投递
无需 ACK
消息可能丢失
生产者
消息队列
消费者
丢弃

实现方式

  • 生产者不等待确认(fire-and-forget)
  • 消费者不发送 ACK
  • 消息队列收到即丢弃,不持久化

适用场景:监控指标上报、实时视频流、可容忍丢失的日志

3.2 至少一次 (At Least Once)

实现方式

  • 生产者:同步发送 + 重试机制,等待队列 ACK
  • 消息队列:消息持久化到磁盘,等待消费者 ACK 后才删除
  • 消费者:处理完成后发送 ACK,如果超时或失败,队列重新投递

关键配置(以 Kafka 为例):

properties 复制代码
# 生产者:等待所有副本确认
acks=all
retries=3

# 消费者:手动提交 offset
enable.auto.commit=false

3.3 恰好一次 (Exactly Once)

实现方式 :通常是 "至少一次" + "幂等消费" 的组合。
确保
确保
至少一次投递
消息不丢
幂等消费
消息不重
恰好一次

技术方案

方案 说明 代表系统
分布式事务 2PC / 3PC 协议 传统数据库
事务消息 消息队列内置事务 RocketMQ、Kafka 事务
幂等设计 业务层去重(更常用) 数据库唯一键、Redis 去重
去重表 消费者记录处理过的消息 ID 自定义实现

4.主流消息队列的投递保证

消息队列 默认模型 支持的模型 说明
Kafka 至少一次 至多一次、恰好一次(事务) 通过 acks 和 enable.auto.commit 配置
RocketMQ 至少一次 至多一次、恰好一次 同步刷盘 + 同步复制保证不丢
RabbitMQ 至少一次 至多一次 通过 publisher confirms + consumer ack
Pulsar 至少一次 至多一次、恰好一次 支持 Exactly Once 语义
AWS SQS 至少一次 --- FIFO 队列支持 Exactly Once

5.实际开发中的选择策略

可以
不可以
可以
不可以
业务需求
消息可以丢失吗?
至多一次
可以容忍重复吗?
至少一次 + 业务去重
恰好一次

幂等 + 事务/去重表
日志、监控
订单、支付
金融、对账

业务类型 推荐模型 理由
金融交易 恰好一次 数据绝对准确,不能丢不能重
订单支付 至少一次 + 幂等 不能丢,重复可通过订单号去重
日志采集 至多一次 丢几条日志可接受,性能优先
用户行为分析 至少一次 宁可重复统计,不能丢数据

6.总结

模型 核心保证 实现代价 典型场景
至多一次 不重复,可能丢 最低 监控、实时视频
至少一次 不丢失,可能重复 中等 大多数业务场景
恰好一次 不丢不重 最高 金融、对账

一句话总结 :"至少一次"意味着"消息绝对不会丢,但你可能会收到多次"------所以消费者的幂等处理是标配。如果你能做到"至少一次 + 幂等",就等价于实现了"恰好一次"。


参考文献

AMQP: Home
Kafka 协议规范

相关推荐
Apache RocketMQ3 天前
RocketMQ源码解析——秒级定时消息介绍
java·云原生·消息队列·rocketmq·java-rocketmq
SuperherRo6 天前
服务攻防-处理平台安全&消息队列&ActiveMQ&RocketMQ&Kafka&Spring包&CVE复现
kafka·消息队列·rocketmq·activemq
Filwaod8 天前
Java面试:AIGC场景下的技术深度拷问-谢飞机篇
spring boot·缓存·微服务·消息队列·aigc·java面试·ai技术
轻刀快马8 天前
穿透 MQ 专栏 (五):【终局之战】MySQL 和 MQ 的世纪联姻:扒开“分布式事务”的遮羞布
数据库·分布式·消息队列
轻刀快马9 天前
穿透 MQ 专栏 (四):【秩序之争】被杀死的全局顺序:消息乱序与千万级积压的救火指南
消息队列
小白君65310 天前
互联网大厂Java面试:从Spring Boot到微服务的技术场景深度解析
spring boot·redis·微服务·消息队列·java面试·数据库优化
轻刀快马10 天前
穿透 MQ 专栏 (一):【架构觉醒】服务器又被打挂了?用 MQ 筑起拦下十万并发的“三峡大坝”
消息队列·mq
Qt程序员13 天前
【无标题】
linux·c++·消息队列·共享内存·c/c++·管道·信号量
无籽西瓜a19 天前
【西瓜带你学Kafka | 第六期】Kafka 生产确认、消费 API 与分区分配策略(文含图解)
java·分布式·后端·kafka·消息队列·mq
无籽西瓜a19 天前
【西瓜带你学Kafka | 第七期】Kafka 日志存储体系:保留清理、消息格式与分段刷新策略(文含图解)
java·分布式·后端·kafka·消息队列·mq