Kafka中的恰好一次交付

Kafka是如何做到消息不重复交付,不丢失,恰好发送一次的

Kafka支持三种交付语义,至少一次(At least once semantics)、至多一次(At most once semantics)、恰好发送一次(Exactly once semantics),这篇文章讲解了Kafka是如何实现第三种的

任何时候都可能发生Broker节点下线,针对这种情况,Kafka保证了每条消息被持久化并且冗余多份(作者按:建议约定topic的配置replication.factor参数值必须大于1,要求每个 partition必须有至少2个副本)。不过也有可能生产者到Broker节点的RPC失败(The producer-to-broker RPC can fail),如果此时消息已经写入Broker,但是还没来得及给生产者发送ACK确认就崩溃了,生产者无法感知原因,它只能等待一段时间后重试,最后导致消费者重复消费。另外一种情况就是客户端也可能会失败。所以在错综复杂的环境中,确保只交付一次是非常有挑战的工程问题

Kafka 0.11.x解决了这个问题,重点是下面提到的三点

1、幂等性(Idempotence: Exactly once in order semantics per partition)

每批消息将包含一个序列号,通过序列号校验过滤重复消息,即使主节点挂了,重新选举的节点也能感知到是否有重复(this sequence number is persisted to the replicated log, so even if the leader fails, any broker that takes over will also know if a resend is a duplicate),可以通过设置在生产端配置”enable.idempotence=true”开启这个功能。作者按:建议约定服务端配置min.insync.replicas参数必须大于1,要求leader感知至少还有一个follower保持心跳连接

2、事务(Transactions: Atomic writes across multiple partitions)

需要保证跨多个Topic-Partition的数据要么全部写入成功,要么全部失败,不会出现中间状态,才能保证幂等性写入

producer.initTransactions();
try {
  producer.beginTransaction();
  producer.send(record1);
  producer.send(record2);
  producer.commitTransaction();
} catch(ProducerFencedException e) {
  producer.close();
} catch(KafkaException e) {
  producer.abortTransaction();
}
复制代码

需要注意的是,消费者可能读取到的是中间状态,不过可以配置参数isolation.level=read_committed,表示事务提交后可获取,另外一个可选值是read_uncommitted,表示不等待事务提交,正常通过offset order读取

3、流处理(The real deal: Exactly once stream processing in Apache Kafka)

数据处理失败后,还能访问到原始输入数据,然后再执行处理操作,也就是说保证输入数据源的可依赖性才能保证恰好一次正确交付。Kafka是通过将数据透明地fold起来合并成原子性操作以事务的形式写入多个分区,如果失败就回滚关联状态,不需要重新设置offset,整个过程不涉及原始数据获取的幂等性操作

文章来源:www.liangsonghua.me

作者介绍:京东资深工程师-梁松华,长期关注稳定性保障、敏捷开发、JAVA高级、微服务架构

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务