一、getWorkunits()
该函数是重写的抽象类Source中的getWorkunits(),划分Work Unit过程由此开始。
函数实例化KafkaWrapper用于访问Kafka,获取相关信息。KafkaWrapper是Gobblin实现的一个集成了多种KafkaApi的封装。
Source为每一个Topic建立一个WorkUnitCreator线程用于将Topic的抽取任务,该类继承了Runable,调用getWorkUnitsForTopic()划分为Work Unit。
在WorkUnitCreator过程中会跳过一部分partition(具体原因在后面阅读Work Unit创建函数时分析),通过createEmptyWorkUnitsForSkippedPartitions函数创建一个空的实际不执行的WorkUnit,这是为了在任务完成后,仍然记录该partition的相关信息。
最后,从配置文件中获取最大Map数,将WorkUnit合并成为等同于该数量的MultiWorkunits。
二、getWorkUnitsForTopic()
在Kafka Source的实现中isTopicQualified直接返回true,继承自KafkaSource的类可以重写该方法对topic进行筛选。如果返回值是false,调用skipWorkUnit函数跳过该topic的WorkUnit。
函数中调用getWorkUnitForTopicPartition()为每一个partition创建WorkUnit。最后将全部的WorkUnit加入Unit列表。
三、getWorkUnitForTopicPartition()
该函数是一个重载函数,有两组传入参数,分别为(KafkaPartition,SourceState, Optional<State>)及(KafkaPartition,Offsets, Optional<State>)。
(一)KafkaPartition, SourceState, Optional<State>
在getWorkUnitsForTopic()调用的是这一组参数。程序主要获取EarliestOffset、LatestOffset、StartOffset流程如下:
1、试图从Kafka集群获取指定分区的Offset信息
通过KafkaWrapper调用KafkaAPI获取指定分区的Offset,如果获取失败,则将failedToGetKafkaOffsets标记为true。
2、从Gobblin存储的信息中获取上一次读取位置
从上一Job执行完成信息中获取previousOffset,如果获取失败,则将previousOffsetNotFound标记为true。
3、根据Offset获取情况,决定本次是否读取数据及从何位置开始读取
(1)如果failedToGetKafkaOffsets为true
没有从Kafka集群获取到指定partition的Offset信息,本次显然无法读取该partition。区分是否读取到上一次执行信息中的Offset,如果没有,则直接返回null,跳过此Unit,如果有,则调用createEmptyWorkUnit()创建一个空的Unit,以在此次执行保存读取位置信息。
(2)判断topics.move.to.latest.offset
如果为true,调用offsets.startAtLatestOffset()将StartOffset设为LatestOffset。
如果为false,且未能读取到previousOffset,由bootstrap.with.offset决定StartOffset为EarliestOffset还是LatestOffset。
如果为false,且读取到previousOffset,则StartOffset为previousOffset,如果previousOffset超出partition的Offset范围,则根据配置参数选取offset。
(3)调用getWorkUnitForTopicPartition()
以(KafkaPartition, Offsets, Optional<State>)调用getWorkUnitForTopicPartition()函数,创建WorkUnit。
(二)(KafkaPartition,Offsets,Optional<State>)调用
实例化Extract,并以Extract实例化workUnit。然后将相关参数配置加入workUnit。
四、总结
Kafka Source主要工作就是获取Topic、Partition、Offset,并依据这些信息确定本次读取内容,并划分WorkUnit。