编辑导读:本篇是 Kafka Producer 原理剖析的下篇,对 Kafka Producer的幂等性、实现细节和常见问题进行了详细的解读。AutoMQ 是与 Apache Kafka 100% 完全兼容的新一代 Kafka,可以帮助用户降低90%以上的Kafka成本并且进行极速地自动弹性。作为 Kafka 生态的忠实拥护者,我们也会持续致力于传播 Kafka 技术,欢迎关注我们。
引言
在之前的文章《原理剖析:一文搞懂 Kafka Producer(上)》中,我们介绍了介绍 Kafka Producer 的使用方法与实现原理。这篇将继续介绍 Kafka Producer 的实现细节与常见问题。
幂等性
在一个分布式的消息系统中,各个角色均有可能发生故障。以 Apache Kafka 为例,Broker 和 Client 都有可能会崩溃,Broker 与 Client 之间的网络请求与响应都有可能丢失。根据 Producer 处理这类故障时采取的策略,可以分为以下几种语义:
-
至少一次(At Least Once):当发生请求超时或者服务端错误时,Producer 重复尝试发送消息直至成功。这样做可以保证每条消息都被写入 Topic,但是可能会发生重复。
-
至多一次(At Most Once):在超时或报错时 Producer 不进行重试,每条消息仅发送一次。这样做可以避免消息重复,但也可能会导致消息丢失。
-
精确一次(Exactly Once):Producer 进行适当的重试,以确保每条消息会且仅会被写入 Topic 一次,既不重复,也不遗漏。Exactly Once 的语义是最理想的实现,它可以满足绝大多数业务场景的需求;但同时也是最难以实现的,它需要 Client 与 Broker 之间的密切配合。
Apache Kafka Producer 提供了两个级别的 Exactly Once 的语义实现:
-
幂等性(Idempotence):确保 Producer 在向某个 Partition 发送消息时,该消息会且仅会被持久化一次。
-
事务性(Transaction):当 Producer 同时向多个 Partition 发送消息时,确保这些消息要么都被持久化,要么都不被持久化。
这里我们主要介绍 Kafka Producer 幂等性的使用与实现,关于事务消息的实现原理可以参阅我们之前的文章《原理剖析| Kafka Exactly Once 语义实现原理:幂等性与事务消息》。
开启幂等性
Kafka Producer 开启幂等性是非常简单的,它只需要设置几个配置项,而无需修改任何其他代码(Producer 的接口并没有变化)。
相关配置项有:
-
acks
当指定数量的副本收到消息后,Producer 才会认为消息写入完成。默认为 “all”
-
acks=0
Producer 不会等待任何 broker 的响应,消息写入网络层后即认为写入成功
-
acks=1
Producer 会等待 leader broker 的响应
-
acks=all
Producer 会等待所有同步的(in sync)副本的响应
-
-
enable.idempotence
开启幂等性,即,保证每条消息写入且仅被写入一次,同时保证消息按照发送顺序写入。默认为 “true”
开启此配置时,需要保证 max.in.flight.requests.per.connection 不大于 5,retries 大于 0,acks 设置为 “all”
在使用时需要注意,幂等 Producer 仅能避免由 Producer 内部的重试策略(Producer、Broker 或网络出错)导致的消息重复,它无法处理以下几种情况:
-
幂等 Producer 仅保证 Session 级别的不重不漏,当 Producer 发生重启时,不能保证重启后与重启前发送的消息不重复。
-
幂等 Producer 仅保证 Partition 级别的不重不漏,不能保证向多个 Partition 发送的消息不重复。
-
当 Producer 出于各种原因发送超时时,即,发送耗时超过了 delivery.timeout.ms,Producer 会抛出
TimeoutException;此时无法保证对应的消息是否已经被 Broker 持久化,需要由上层根据情况进行处理。
实现原理
为了实现幂等性,Kafka 引入了以下两个概念:
-
Producer ID(以下简称 PID):Producer 的唯一标识。PID 由 Idempotent Producer 在首次发送消息前,请求 Broker 分配获得,是全局唯一的。PID 仅在 Producer 和 Broker 内部使用,不会暴露给 Client 使用者。
-
Sequence Number(以下简称 SEQ):消息的序列号。该序列号在 (PID, Partition) 维度上严格递增。事实上,SEQ 会存储在 Record Batch 的头中,作为 Batch 中第一条消息的 SEQ,Batch 中其它消息的 SEQ 依次递增。
值得一提的是,PID 与 SEQ 均会跟随消息持久化到 Log 中。
事实上,除了前述两个属性外,还有 Producer Epoch,它与 PID 结合才会唯一标识一个 Producer。它的在不同的场景下有不同的用途:
对于开启事务能力的 Producer(配置了 “transactional.id”),Producer Epoch 同样由 Broker 分配。这样做可以保证,多个具有相同 Transactional ID 的 Producer 中仅会有一个生效,即 “Fence Producer”。

最低0.47元/天 解锁文章
1487

被折叠的 条评论
为什么被折叠?



