kafka--基础知识点--5.2--最多一次、至少一次、精确一次

个人理解,仅供参考。一个消息的传递可以分两个过程,a) producer发送消息到 broker,b) consumer从broker读消息并发送。因此对于三种消息的传递策略要分两个阶段来看:

  • a) producer发送消息到 broker,对于producer来说,有自己的最多一次、至少一次、精确一次策略;在生产者发送消息过程中broker崩溃、网络抖动、生产者崩溃会用到这些策略、分区发生变化。
  • b) consumer从broker读消息并发送,对于consumer来说,也有自己的最多一次、至少一次、精确一次策略;在消费者消费消息过程中如果消费者发生崩溃会用到这些策略。

1 producer端

1.1 ACK策略

Kafka 通过 acks 参数控制消息确认机制,可实现三种消息传递语义:至多一次(At-Most-Once)至少一次(At-Least-Once)恰好一次(Exactly-Once) 。以下是详细对应关系:
kafka--基础知识点--5.1--ACK机制

1.1.1 至多一次(At-Most-Once)

  • 语义:消息可能丢失,但绝不会重复。
  • ACK策略acks=0
    • 生产者不等待 Broker 确认,发送后立即继续下一条消息。
    • 若 Broker 未收到消息或崩溃,消息丢失且不会重试。
  • 配置要求
    • 生产者:acks=0retries=0(禁用重试)。
  • 适用场景:实时性要求高但允许少量数据丢失的场景(如日志采集)。

1.1.2 至少一次(At-Least-Once)

  • 语义:消息绝不会丢失,但可能重复。
  • ACK策略acks=all
    • acks=all:生产者等待所有 ISR 副本确认,确保消息持久化,结合重试可避免丢失。
  • 配置要求
    • 生产者:acks=allretries=Integer.MAX_VALUE(无限重试)。
  • 适用场景:允许重复但不容忍丢失的场景(如支付状态更新)。

1.1.3 精确一次(Exactly-Once)

  • 语义:消息不丢失、不重复。
  • ACK策略acks=all + 幂等性 + 事务
  • 配置要求
    • 生产者:acks=allretries=Integer.MAX_VALUEenable.idempotence=true
  • 适用场景:要求严格一致性的场景(如金融交易、流处理)。

2 consumer端

kafka--基础知识点--9.1--consumer 至多一次、至少一次、精确一次

2.1 读取策略

isolation.level=read_committed:当消费者设置该参数时,表示消费者仅消费已提交的事务消息。该参数只有当生产者使用事务时消费者设置该参数才有效。

2.2 提交策略

2.2.1 自动提交

如果消费者设置为自动提交(enable.auto.commit参数设置为true),Kafka 消费者后台线程每隔 auto.commit.interval.ms 自动提交最近一次 poll() 的 offset,而不管消费者是否对消息是否已经成功进行了业务处理。可能会导致消息丢失[先自动提交,但消息还没处理完成消费者崩溃],也可能导致重复消费[先消费完成,但提交前消费者崩溃,重启消费者后,再次消费同一条消息]。

2.2.2 手动提交

2.2.2.1. 至多一次 (At-Most-Once):

行为: 消费者读取消息后,先提交位移,然后再进行业务处理。

风险: 如果业务处理逻辑在提交位移之后失败,这条消息就永远不会被再次处理了(因为位移已经前移)。导致消息丢失。

2.2.2.2 至少一次 (At-Least-Once):

行为: 消费者读取消息后,先进行业务处理,处理成功后再提交位移。

风险: 如果业务处理成功,但在提交位移之前消费者崩溃了,当消费者恢复后,它会从上一次提交的位移重新消费,导致消息被重复处理。导致消息重复。

2.2.2.3 精确一次 (Exactly-Once)
2.2.2.3.1 幂等性消费 (Idempotent Consumption) - 推荐且最常用

思路:承认消息可能会被重复传递,但从业务逻辑上保证重复执行不会产生负面效果。

做法:在消费者的处理逻辑中,设计幂等性。例如:

为每条消息生成一个唯一 ID(可以是消息key,或自定义UUID)。

在处理前,先检查这个 ID 是否已经被处理过(比如在数据库里查一下)。

如果已处理,就直接跳过并提交位移(视为成功);如果未处理,则执行业务逻辑。

这是最有效、最通用的方法,因为它不依赖于任何特定技术,而是从业务设计上根本性地解决问题。

例如:

a) 对于流程中的消息,每条消息中包含唯一id,比如业务id,在数据库中将业务id作为Unique key,插入重复时会报duplicate key异常,不会导致数据库中出现脏数据

b) Redis中使用set存储业务id,天然幂等性

c) 如果不是上面两个场景,需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下消费过吗?如果没有消费过,就执行相应业务进行处理,然后这个 id 写 Redis,最后提交偏移。如果消费过了,那如果消费过了,那就别处理了,保证不重复处理相同的消息即可

2.2.2.3.2 事务性输出 (Transactional Output) / 两阶段提交 (2PC) - 复杂且受限

思路:将消费者的"业务处理"和"位移提交"绑定为一个分布式事务。

做法:例如,使用 Kafka 的事务性生产者,将处理结果和位移提交到外部系统(如另一个Kafka主题)的操作放在一个事务里。但这通常需要外部系统(如数据库)也支持参与 Kafka 事务(通过 Kafka Connect),实现复杂度非常高,性能和可用性也会受影响。不推荐普通应用使用。

3 一条消息从发送到bocker,再被消费者从bocker消费整个过程保证精确一次需要

  • producer 使用精确一次策略,即1.1.3
  • consumer
    • consumer 设置'isolation.level': 'read_committed',该参数使得consumer只会消费producer事务已提交事务的消息。
    • consumer使用精确一次策略,即 2.2.2.3
相关推荐
程序消消乐2 小时前
Kafka 入门指南:从 0 到 1 构建你的 Kafka 知识基础入门体系
分布式·kafka
智能化咨询2 小时前
Kafka架构:构建高吞吐量分布式消息系统的艺术——进阶优化与行业实践
分布式·架构·kafka
在未来等你9 小时前
Elasticsearch面试精讲 Day 17:查询性能调优实践
大数据·分布式·elasticsearch·搜索引擎·面试
大数据CLUB12 小时前
基于spark的澳洲光伏发电站选址预测
大数据·hadoop·分布式·数据分析·spark·数据开发
ajax_beijing14 小时前
zookeeper是啥
分布式·zookeeper·云原生
RestCloud17 小时前
Kafka实时数据管道:ETL在流式处理中的应用
数据库·kafka·api
AAA修煤气灶刘哥19 小时前
Kafka 入门不踩坑!从概念到搭环境,后端 er 看完就能用
大数据·后端·kafka
若鱼191919 小时前
spring-kafka消费异常处理
spring·kafka
若鱼191919 小时前
Kafka如何配置生产者拦截器和消费者拦截器
java·kafka