Kafka通过timestamp获取offset的机制详解

来源:互联网 发布:淘宝游戏充值赚钱吗 编辑:程序博客网 时间:2024/06/05 10:23


1、入口

Kafka Server 处理 Client 发送来的请求的入口在

文件夹:  core/src/main/scala/kafka/server

类:kafka.server.KafkaApis

方法: handle

处理offset请求的函数: handleOffsetRequest

2、处理逻辑


处理逻辑主要分为四步

  • 获取partition
  • 从partition中获取offset
  • high water mark 处理(这一段的资料太少了)
  • 异常处理

由于request中包含查询多个partition的offset的请求。所以最终会返回一个map,保存有每个partition对应的offset

这里主要介绍从某一个partition中获取offset的逻辑,代码位置

  • kafka.log.Log#getOffsetsBefore(timestamp, maxNumOffsets)

从一个partition中获取offset

1、建立offset与timestamp的对应关系,并保存到数据中

//每个Partition由多个segment file组成。获取当前partition中的segment列表

val segsArray = segments.view

 

// 创建数组

var offsetTimeArray: Array[(Long, Long)] =null

if(segsArray.last.size >0)

  offsetTimeArray =newArray[(Long, Long)](segsArray.length +1)

else

  offsetTimeArray =newArray[(Long, Long)](segsArray.length)

 

// 将 offset 与 timestamp 的对应关系添加到数组中

for(i <-0until segsArray.length)

  // 数据中的每个元素是一个二元组,(segment file 的起始 offset,segment file的最近修改时间)

  offsetTimeArray(i) = (segsArray(i).start, segsArray(i).messageSet.file.lastModified)

if(segsArray.last.size >0)

  // 如果最近一个 segment file 不为空,将(最近的 offset, 当前之间)也添加到该数组中

  offsetTimeArray(segsArray.length) = (logEndOffset, time.milliseconds)

通过这段逻辑,获的一个数据 offsetTimeArray,每个元素是一个二元组,二元组内容是(offset, timestamp)

 

2、找到最近的最后一个满足 timestamp < target_timestamp 的 index。


var startIndex = -1

timestamp match {

  // 需要查找的 timestamp 是 -1 或者 -2时,特殊处理

  caseOffsetRequest.LatestTime =>  // OffsetRequest.LatestTime = -1

    startIndex = offsetTimeArray.length -1

  caseOffsetRequest.EarliestTime => // OffsetRequest.EarliestTime = -2

    startIndex =0

  case_ =>

    var isFound =false

    debug("Offset time array = "+ offsetTimeArray.foreach(o =>"%d, %d".format(o._1, o._2)))

    startIndex = offsetTimeArray.length -1  // 从最后一个元素反向找

    while(startIndex >=0&& !isFound) {    // 找到满足条件或者

      if(offsetTimeArray(startIndex)._2 <= timestamp)  // offsetTimeArray 的每个元素是二元组,第二个位置是 timestamp

        isFound =true

      else

        startIndex -=1

    }

}

通过这段逻辑,实际找到的是 “最近修改时间早于目标timestamp的最近修改的segment file的起始offset”

但是获取offset的逻辑并没有结束,后续仍有处理


3、找到满足该条件的offset数组


实际上这个函数的功能是找到一组offset,而不是一个offset。第二个参数 maxNumOffsets 指定最多找几个满足条件的 offset。

获取一组offset的逻辑

// 返回的数据的长度 = min(maxNumOffsets, startIndex + 1),startIndex是逻辑2中找到的index

val retSize = maxNumOffsets.min(startIndex +1)

val ret = newArray[Long](retSize)

 

// 逐个将满足条件的offset添加到返回的数据中

for(j <-0until retSize) {

  ret(j) = offsetTimeArray(startIndex)._1

  startIndex -=1

}

 

// 降序排序返回。offset 越大数据越新。

// ensure that the returned seq is in descending order of offsets

ret.toSeq.sortBy(- _)

最终返回这个数组


3、注意事项


  • 实际找到的offset并不是从目标timestamp开始的第一个offset。需要注意
  • 当 timestamp 小于最老的数据文件的最近修改时间时,返回值是一个空数组。可能会导致使用时的问题。
  • 调整segment file文件拆分策略的配置时,需要注意可能会造成的影响。


引用公司董董wiki分享

0 0
原创粉丝点击