Gobblin Kafka Source源码分析

来源:互联网 发布:mac的网页开发工具 编辑:程序博客网 时间:2024/05/16 11:30

  Kafka Source主要任务是根据配置文件指定的Topic,读取相应的信息,划分Work Unit。

一、getWorkunits()

  该函数是重写的抽象类Source中的getWorkunits(),划分Work Unit过程由此开始。

函数实例化KafkaWrapper用于访问Kafka,获取相关信息。KafkaWrapper是Gobblin实现的一个集成了多种KafkaApi的封装。

    for (KafkaTopic topic : topics) {      threadPool.submit(new WorkUnitCreator(topic, state,          Optional.fromNullable(topicSpecificStateMap.get(topic.getName())), workUnits));    }

  Source为每一个Topic建立一个WorkUnitCreator线程用于将Topic的抽取任务,该类继承了Runable,调用getWorkUnitsForTopic()划分为Work Unit。

  在WorkUnitCreator过程中会跳过一部分partition(具体原因在后面阅读Work Unit创建函数时分析),通过createEmptyWorkUnitsForSkippedPartitions函数创建一个空的实际不执行的WorkUnit,这是为了在任务完成后,仍然记录该partition的相关信息。

int numOfMultiWorkunits =        state.getPropAsInt(ConfigurationKeys.MR_JOB_MAX_MAPPERS_KEY, ConfigurationKeys.DEFAULT_MR_JOB_MAX_MAPPERS);    return KafkaWorkUnitPacker.getInstance(this,state).pack(workUnits, numOfMultiWorkunits);

  最后,从配置文件中获取最大Map数,将WorkUnit合并成为等同于该数量的MultiWorkunits。

二、getWorkUnitsForTopic()

  private List getWorkUnitsForTopic(KafkaTopic topic, SourceState state, Optional topicSpecificState) {    boolean topicQualified = isTopicQualified(topic);    List workUnits = Lists.newArrayList();    for (KafkaPartition partition : topic.getPartitions()) {      WorkUnit workUnit = getWorkUnitForTopicPartition(partition, state, topicSpecificState);      this.partitionsToBeProcessed.add(partition);      if (workUnit != null) {        if (!topicQualified) {          skipWorkUnit(workUnit);        }        workUnits.add(workUnit);      }    }    return workUnits;  }

  在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信息

 try {      offsets.setEarliestOffset(this.kafkaWrapper.getEarliestOffset(partition));      offsets.setLatestOffset(this.kafkaWrapper.getLatestOffset(partition));    } catch (KafkaOffsetRetrievalFailureException e) {      failedToGetKafkaOffsets = true;    }

通过KafkaWrapper调用KafkaAPI获取指定分区的Offset,如果获取失败,则将failedToGetKafkaOffsets标记为true。

  2、从Gobblin存储的信息中获取上一次读取位置

long previousOffset = 0;    boolean previousOffsetNotFound = false;    try {      previousOffset = getPreviousOffsetForPartition(partition, state);    } catch (PreviousOffsetNotFoundException e) {      previousOffsetNotFound = true;    }

  从上一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>)调用

  private WorkUnit getWorkUnitForTopicPartition(KafkaPartition partition, Offsets offsets,      Optional topicSpecificState) {    Extract extract = this.createExtract(DEFAULT_TABLE_TYPE, DEFAULT_NAMESPACE_NAME, partition.getTopicName());    WorkUnit workUnit = WorkUnit.create(extract);    if (topicSpecificState.isPresent()) {      workUnit.addAll(topicSpecificState.get());    }    workUnit.setProp(TOPIC_NAME, partition.getTopicName());    workUnit.setProp(ConfigurationKeys.EXTRACT_TABLE_NAME_KEY, partition.getTopicName());    workUnit.setProp(PARTITION_ID, partition.getId());    workUnit.setProp(LEADER_ID, partition.getLeader().getId());    workUnit.setProp(LEADER_HOSTANDPORT, partition.getLeader().getHostAndPort().toString());    workUnit.setProp(ConfigurationKeys.WORK_UNIT_LOW_WATER_MARK_KEY, offsets.getStartOffset());    workUnit.setProp(ConfigurationKeys.WORK_UNIT_HIGH_WATER_MARK_KEY, offsets.getLatestOffset());    return workUnit;  }

  实例化Extract,并以Extract实例化workUnit。然后将相关参数配置加入workUnit。

  四、总结

  Kafka Source主要工作就是获取Topic、Partition、Offset,并依据这些信息确定本次读取内容,并划分WorkUnit。

0 0