揭秘 Kafka 的恰好一次语义:一份综合指南

通过幂等生产者、事务、read_committed消费者和偏移量提交,理解Kafka的精确一次语义。

解密Kafka的精确一次语义:全面指南

Kafka的精确一次语义可以在生产者重试、代理故障转移或应用程序重启时,保护流处理管道免受重复输出记录的影响。这种保证很强大,但比字面意思要窄:Kafka可以使Kafka写入和消费的偏移量具有事务性。它无法自动使你的外部数据库、支付网关或HTTP API实现精确一次。

当重复输出代价高昂或难以清理时,使用精确一次语义,例如库存调整、账户余额事件或由其他服务消费的派生状态主题。

通俗解释交付保证

Kafka应用程序通常讨论三种交付模型。

  • 至多一次:你的应用可能会丢失记录,但不应处理同一条记录两次。当偏移量在处理完成之前提交时,可能会发生这种情况。
  • 至少一次:你的应用不应丢失记录,但在重试或重启后可能会多次处理同一条记录。
  • 精确一次:Kafka的读取-处理-写入循环将其输出记录和消费的偏移量作为一个事务提交。

最后一点是关键。当应用程序从Kafka读取数据,将结果写回Kafka,并在同一事务中提交偏移量时,精确一次语义最强。

幂等生产者

幂等生产者防止由生产者重试引起的重复写入。Kafka为生产者分配一个ID,并跟踪每个生产者和分区的序列号。如果代理已经接受了一个批次,然后收到重试请求,它可以拒绝重复的批次,而不是再次追加。

对于当前的Kafka客户端,当你没有配置冲突的生产者设置时,幂等性默认启用。你仍然可以显式设置它:

enable.idempotence=true
acks=all

acks=all意味着领导者在确认写入之前等待所有同步副本。幂等性还依赖于兼容的重试和飞行中请求设置,因此除非你了解在你的客户端版本中的影响,否则避免覆盖生产者可靠性设置。

幂等性保护生产者重试,但不能使整个处理工作流原子化。如果你的应用从一个主题消费并生产到另一个主题,你需要事务来将输出和偏移量提交绑定在一起。

Kafka事务

事务允许一个生产者将多个写入分组为一个原子单元。生产者需要一个稳定的transactional.id

transactional.id=inventory-adjuster-0
enable.idempotence=true
acks=all

典型的事务流程是:

  1. 应用程序启动时初始化事务。
  2. 开始一个事务。
  3. 从输入主题消费记录。
  4. 生产输出记录。
  5. 将消费的偏移量发送到事务中。
  6. 提交事务,或在失败时中止。

如果进程在提交前崩溃,Kafka不会将未提交的输出暴露给read_committed消费者。重启后,应用程序可以再次读取相同的输入记录,并产生一个已提交的结果。

重要的消费者设置

读取事务性输出的消费者应使用:

isolation.level=read_committed
enable.auto.commit=false

read_committed隐藏来自已中止事务的记录。enable.auto.commit=false防止消费者在事务外部提交偏移量。

属性名称很重要。Kafka的消费者设置是enable.auto.commit,而不是auto.commit.enable

对于手动消费者-生产者应用程序,偏移量提交必须是生产者事务的一部分。在Java客户端中,这意味着使用事务性生产者API,包括在提交事务之前将偏移量发送到事务中。

具体场景

想象一个orders主题和一个inventory-events输出主题。你的服务读取一个订单,检查SKU,并写入一个库存扣减事件。

没有事务时,在写入输出后但在提交输入偏移量前崩溃,重启后可能会创建重复的扣减。有了事务,输出事件和输入偏移量提交要么一起成功,要么一起失败。重启可能会重新读取订单,但只有一个已提交的库存事件对下游的read_committed消费者可见。

需要注意的限制

Kafka的精确一次语义不涵盖Kafka之外的副作用,除非你为此设计。如果同一个服务也写入PostgreSQL或调用计费API,该外部副作用需要自己的幂等键、唯一约束、事务策略或发件箱模式。

事务还会增加协调开销。对于简单的日志摄取,如果重复是可接受的,幂等生产者加上至少一次消费者可能就足够了。

实用检查清单

为每个应用程序实例或任务使用稳定的transactional.id。不要让两个活跃的生产者同时使用相同的transactional ID。

将事务性输出的消费者设置为read_committed。在事务性处理循环中禁用自动偏移量提交。

保持事务简短。大型事务可能会增加延迟并使恢复变慢。

单独处理外部系统。Kafka可以保护Kafka状态,但你的数据库写入仍然需要幂等设计。

有用的要点:精确一次语义不是魔法开关。它们是一组生产者、消费者和事务选择,最适合Kafka到Kafka的流处理。