0%

kafka offset 机制

Kafka 的消息投递保证(delivery guarantee)机制以及如何实现?


概念

什么是 offset?

offset 是 consumer position,Topic 的每个 Partition 都有各自的 offset

消费者需要自己保留一个 offset,从 kafka 获取消息时,只拉去当前 offset 以后的消息

Kafka 的 scala/java 版的 client 已经实现了这部分的逻辑

之前 offset 保存到 zookeeper 上,broker 存放 offset 是 kafka 从 0.9 版本开始,提供的新的消费方式原因是zookeeper来存放,还是有许多弊端,不方便灵活控制,效率不高

offset 记录位置

kafka 消费者在会保存其消费的进度,也就是offset,存储的位置根据选用的 kafka api 不同而不同。具体可以参看kafka 消费者offset记录位置和方式

Kafka 支持三种消息投递语义

  • At most once 消息可能会丢,但绝不会重复传递
  • At least one 消息绝不会丢,但可能会重复传递
  • Exactly once 每条消息肯定会被传输一次且仅传输一次

consumer在从broker读取消息后,可以选择commit,该操作会在Zookeeper中存下该consumer在该partition下读取的消息的offset,该consumer下一次再读该partition时会从下一条开始读取。如未commit,下一次读取的开始位置会跟上一次commit之后的开始位置相同。

可以将consumer设置为autocommit,即consumer一旦读到数据立即自动commit。如果只讨论这一读取消息的过程,那Kafka是确保了Exactly once。但实际上实际使用中consumer并非读取完数据就结束了,而是要进行进一步处理,而数据处理与commit的顺序在很大程度上决定了消息从broker和consumer的delivery guarantee semantic。

·读完消息先commit再处理消息。这种模式下,如果consumer在commit后还没来得及处理消息就crash了,下次重新开始工作后就无法读到刚刚已提交而未处理的消息,这就对应于At most once。

·读完消息先处理再commit消费状态(保存offset)。这种模式下,如果在处理完消息之后commit之前Consumer crash了,下次重新开始工作时还会处理刚刚未commit的消息,实际上该消息已经被处理过了,这就对应于At least once。

·如果一定要做到Exactly once,就需要协调offset和实际操作的输出。经典的做法是引入两阶段提交,但由于许多输出系统不支持两阶段提交,更为通用的方式是将offset和操作输入存在同一个地方。比如,consumer拿到数据后可能把数据放到HDFS,如果把最新的offset和数据本身一起写到HDFS,那就可以保证数据的输出和offset的更新要么都完成,要么都不完成,间接实现Exactly once。(目前就high level API而言,offset是存于Zookeeper中的,无法存于HDFS,而low level API的offset是由自己去维护的,可以将之存于HDFS中)。

总之,Kafka默认保证At least once,并且允许通过设置producer异步提交来实现At most once,而Exactly once要求与目标存储系统协作,Kafka提供的offset可以较为容易地实现这种方式。


参考链接