Kafka Exactly Once实现原理

2019-10-30 17:22:37 浏览数 (1)

Kafka的EOS主要体现在3个方面:

  • 幂等producer:保证发送单个分区的消息只会发送一次,不会出现重复消息
  • 事务(transaction):保证原子性地写入到多个分区,即写入到多个分区的消息要么全部成功,要么全部回滚
  • 流处理EOS:流处理本质上可看成是“读取-处理-写入”的管道。此EOS保证整个过程的操作是原子性。注意,这只适用于Kafka Streams

  上面3种EOS语义有着不同的应用范围,幂等producr只能保证单分区上无重复消息;事务可以保证多分区写入消息的完整性;而流处理EOS保证的是端到端(E2E)消息处理的EOS。用户在使用过程中需要根据自己的需求选择不同的EOS。以下是启用方法:

  • 启用幂等producer:在producer程序中设置属性enable.idempotence=true,但不要设置transactional.id。注意是不要设置,而不是设置成空字符串或"null"
  • 启用事务支持:在producer程序中设置属性transcational.id为一个指定字符串(你可以认为这是你的事务名称,故最好起个有意义的名字),同时设置enable.idempotence=true
  • 启用流处理EOS:在Kafka Streams程序中设置processing.guarantee=exactly_once

幂等producer的设计与实现。

-----------------------------------------------------------------------

  所谓幂等producer指producer.send的逻辑是幂等的,即发送相同的Kafka消息,broker端不会重复写入消息。同一条消息Kafka保证底层日志中只会持久化一次,既不会丢失也不会重复。幂等性可以极大地减轻下游consumer系统实现消息去重的工作负担,因此是非常实用的功能。值得注意的是,幂等producer提供的语义保证是有条件的:

  • 单分区幂等性:幂等producer无法实现多分区上的幂等性。如前所述,若要实现多分区上的原子性,需要引入事务
  • 单会话幂等性:幂等producer无法跨会话实现幂等性。即使同一个producer宕机并重启也无法保证消息的EOS语义

  虽然有上面两个限制,幂等producer依然是一个非常实用的新功能。下面我们来讨论下它的设计原理。如果要实现幂等性, 通常都需要花费额外的空间来保存状态以执行消息去重。Kafka的幂等producer整体上也是这样的思想。

  首先,producer对象引入了一个新的字段:Producer ID(下称PID),它唯一标识一个producer,当producer启动时Kafka会为每个producer分配一个PID(64位整数),因此PID的生成和分配对用户来说是完全透明的,用户无需考虑PID的事情,甚至都感受不到PID的存在。其次,0.11 Kafka重构了消息格式,引入了序列号字段(sequence number,下称seq number)来标识某个PID producer发送的消息。和consumer端的offset类似,seq number从0开始计数并严格单调增加。同时在broker端会为每个PID(即每个producer)保存该producer发送过来的消息batch的某些元信息,比如PID信息、消息batch的起始seq number及结束seq number等。这样每当该PID发送新的消息batch时,Kafka broker就会对比这些信息,如果发生冲突(比如起始seq number和结束seq number与当前缓存的相同),那么broker就会拒绝这次写入请求。倘若没有冲突,那么broker端就会更新这部分缓存然后再开始写入消息。这就是Kafka实现幂等producer的设计思路:1. 为每个producer设置唯一的PID;2. 引入seq number以及broker端seq number缓存更新机制来去重。

  • PIDSequence Number的引入实现了写操作的幂等性

PID标记了Producer、topic、partition的唯一性,Seq标记了PID下每一个消息得唯一性。broker会记录已经收到得seq值,下一个seq一定是上一个seq 1,如果大于1,抛InvalidSequenceNumber异常(丢数据了),如果小于1,抛DuplicateSequenceNumber异常(数据重复了),生产端需要对这两种情况做处理

  • 写操作的幂等性结合At Least Once语义实现了单一 Session 内的Exactly Once语义
  • Transaction Marker(引入事务协调者)PID提供了识别消息是否应该被读取的能力,从而实现了事务的隔离性
  • Offset 的更新标记了消息是否被读取,从而将对读操作的事务处理转换成了对写(Offset)操作的事务处理
  • Kafka 事务的本质是,将一组写操作(如果有)对应的消息与一组读操作(如果有)对应的 Offset 的更新进行同样的标记(即Transaction Marker)来实现事务中涉及的所有读写操作同时对外可见或同时对外不可见
  • Kafka 只提供对 Kafka 本身的读写操作的事务性,不提供包含外部系统的事务性

简单的说,其实就是把所有操作都那个小本本写下来,所有人都能看到,就避免了重复问题和丢数据问题。

当然,这只是说kafka内部得事务实现,至于kafka的上游生产者,下游消费者的事务,需要使用方自己实现

参考资料:

https://www.cnblogs.com/huxi2b/p/7717775.html

https://www.infoq.cn/article/kafka-analysis-part-8

0 人点赞