前言
根据源码分析kafka java客户端的生产者和消费者的流程。
基于zookeeper的旧消费者
kafka消费者从消费数据到关闭经历的流程。
由于3个核心线程
- 基于zookeeper的连接器监听该消费者是否触发重平衡,并获取该消费者客户端消费的topic下group对应的partition以及offset。参考` ZookeeperConsumerConnector`
- 寻找partition leader线程循环寻找partition的leader,原理是基于zk的watch,并判断哪些partition的leader发生改变,如果有发生改变,那么创建新的线程来消费这些partition,并把检查之前的线程,如果有消费不是最新的leader那么就shutdown,参考`ConsumerFetcherManager`
- 数据拉取线程通过消费partition的leader,循环拉取数据
解放zookeeper的新消费者
总体思路
- 解放了消费者对zookeeper的依赖,现在只需要和broker交互就可以获取所有需要的元数据。因此,和zk相关的线程都被解放了,取而代之的是基于事件触发思想的流程。
- 减少了不必要的线程,将所有操作lazy化处理,也就是说整个消费流程是基于事件的。比如之前有专门维护心跳的线程,现在改为将心跳任务加入队列,只有在某些事件达到时才会执行。由于在拉取数据时,频繁去检验分区相关元数据会导致拉取性能下降,因此消费者每次在拉取完一批record后,会自动异步去拉取下一批的数据,并更新最新的元数据到内存,而此时使用者正在处理上一批的record,这样处理就保证了基于事件思路下的元数据更新和拉取数据的性能。
拉取数据的两种方式
- 手动分配topic和partition,也就是assign,分配之后,消费者客户端不会感知到其他事件的触发。
- 订阅topic,自动分配到topic的partition,以及该groupid之前消费到的offset,当然也可以自动感知到kafka的rebalance,并获取到相关事件。
拉取流程
- 确保kafka协调者认可了此次消费,并初始化和协调者的连接。认可很多层次的含义,包括kafka集群是否正常,安全认证是否通过之类。
- 确保分区被分配,除了手动assgin的topic,partition和offset,自动subscribe需要从kafka协调者获取相关元数据,也是发生重平衡事件的来源。
- 确保已经获取拉取的offset,否则为从协调者那重新获取对应groupid的offset,如果获取失败(比如这是一个新的groupid),那么会重置offset,根据配置用最旧或者最新来代替。参考`ConsumerCoordinator`
- 拉取数据,通过拉取每个partition的leader,基于NIO思路拉取数据缓存在内存中;参考`Fetcher`。
- 提交offset,如果开启自动提交offset的功能,那么消费者会在两个情况同步提交offset,(1)重平衡或者和broker心跳超时,参考流程2;(2)消费者关闭。如果是手动提交的话可以采用异步或者同步两种提交方式
重平衡触发原因
- 消费的topic的partition数量发生了变化
- topic被创建或者删除
- 有其他消费者客户端使用相同的group来消费或者停止消费同一个topic
数据拉取线程分为两类
kafka集群在同步副本数据的过程,也是基于客户端拉取数据,和消费者稍有不同
- 基于消费者的数据拉取线程
这也就是我们常用的消费者,数据拉取后缓存在内存中给客户端获取
- 基于副本备份的数据拉取线程
kafka集群的副本通过此类线程拉取数据,并写到本地日志中
kafka生产者
相对消费者,生产者的流程简单很多。
生产者采用双线程的方式,主线程也就是用户线程,将数据交给kafka的生产者;另外一个是生产者自己的子线程,将数据源源不断的发送到kafka。
生产流程
- 找到数据发送的partition;
- 将数据按照topic partition分类,每类采用一个双向队列存储数据;检查队列最后一个批是否正在处理,尝试加入到该批,否则创建一个新的批并加入到队列末尾。如果最后一个批已经满了(容量满了或者数据超过1)或者有新的批被创建了,那么触发生产者将数据发送到kafka
- 另外生产者的子线程也会不断的自我循环,判断内存中缓存的数据是否可以被发送到kafka,有5种情况,只要满足其一就把数据发送到broker,full || expired || exhausted || closed || flushInProgress();(1) full参考流程2;(2) expired 距离上一次数据发送的时间超过了一个阈值;(3) exhausted 生产者内部维护了内存队列,记录了当前使用的内存大小,exhausted表示申请的总数>1的情况;(4) closed 生产者被关闭了;(5) 使用者显示的调用了flush()