Kafka日志模块(二):Log定义及初始化

在一个 log 目录下存在多个以“topic-partition”命名的分区目录,每个 topic 分区对应一个 Log 对象,用于管理名下的 LogSegment 对象集合,Log 类使用 SkipList 数据结构对 LogSegment 进行组织和管理。它的定义为

class Log(@volatile var dir: File, // dir 就是这个日志所在的文件夹路径,也就是主题分区的路径
          @volatile var config: LogConfig, // 配置信息
          @volatile var logStartOffset: Long, // log日志文件的当前最早位移
          @volatile var recoveryPoint: Long, // 恢复操作的起始 offset,即 HW 位置,之前的消息已经全部落盘
          scheduler: Scheduler, // 定时任务调度器
          brokerTopicStats: BrokerTopicStats,
          val time: Time,
          val maxProducerIdExpirationMs: Int,
          val producerIdExpirationCheckIntervalMs: Int,
          val topicPartition: TopicPartition,
          val producerStateManager: ProducerStateManager,
          logDirFailureChannel: LogDirFailureChannel) extends Logging with KafkaMetricsGroup {

    // nextOffsetMetadata封装了下一条待插入消息的位移值,基本上可以把这个属性和 Log End Offset 等同起来
    @volatile private var nextOffsetMetadata: LogOffsetMetadata = _ 
    // 取日志高水位值
    @volatile private var highWatermarkMetadata: LogOffsetMetadata = LogOffsetMetadata(logStartOffset)
    // 保存了分区日志下所有的日志段信息, Key 值是日志段的起始位移值,Value 则是日志段对象本身
    private val segments: ConcurrentNavigableMap[java.lang.Long, LogSegment] = new ConcurrentSkipListMap[java.lang.Long, LogSegment]
    // 判断出现 Failure 时是否执行日志截断操作(Truncation),之前靠高水位来判断的机制,可能会造成副本间数据不一致的情形。这里的 Leader Epoch Cache 是一个缓存类数据,里面保存了分区 Leader 的 Epoch 值与对应位移值的映射关系
    @volatile var leaderEpochCache: Option[LeaderEpochFileCache] = None
}

Log 类的初始化逻辑如下:

  locally {
    val startMs = time.milliseconds

    // create the log directory if it doesn't exist
    // 创建分区log路径
    Files.createDirectories(dir.toPath)
    // 初始化leader epoch cache,下面会作详细解释
    initializeLeaderEpochCache()
    // 加载所有日志段对象,返回下一条待出入的消息offset,下面会作详细解释
    val nextOffset = loadSegments()

    /* Calculate the offset of the next message */
    // 更新nextOffsetMetadata
    nextOffsetMetadata = LogOffsetMetadata(nextOffset, activeSegment.baseOffset, activeSegment.size)

    leaderEpochCache.foreach(_.truncateFromEnd(nextOffsetMetadata.messageOffset))
    // 更新Log Start Offset
    updateLogStartOffset(math.max(logStartOffset, segments.firstEntry.getValue.baseOffset))

    // The earliest leader epoch may not be flushed during a hard failure. Recover it here.
    leaderEpochCache.foreach(_.truncateFromStart(logStartOffset))

    // Any segment loading or recovery code must not use producerStateManager, so that we can build the full state here
    // from scratch.
    if (!producerStateManager.isEmpty)
      throw new IllegalStateException("Producer state must be empty during log initialization")
    loadProducerState(logEndOffset, reloadFromCleanShutdown = hasCleanShutdownFile)

    info(s"Completed load of log with ${segments.size} segments, log start offset $logStartOffset and " +
      s"log end offset $logEndOffset in ${time.milliseconds() - startMs} ms")
  }

下面介绍一下initializeLeaderEpochCache初始化leader epoch cache

  private def initializeLeaderEpochCache(): Unit = lock synchronized {
    val leaderEpochFile = LeaderEpochCheckpointFile.newFile(dir)

    // 生成leader epoch cache对象
    def newLeaderEpochFileCache(): LeaderEpochFileCache = {
      val checkpointFile = new LeaderEpochCheckpointFile(leaderEpochFile, logDirFailureChannel)
      new LeaderEpochFileCache(topicPartition, logEndOffset _, checkpointFile)
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值