kafka Log高水位管理操作-源码学习笔记

高水位管理操作

源码定义:


@volatile private var highWatermarkMetadata: LogOffsetMetadata = LogOffsetMetadata(logStartOffset)

根据定义可以看出,1、highWatermarkMetadata是可变的,并且通过volatile 保证它线程之间的可见性。2、高水位值的初始值是 Log Start Offset 的值。3、类型是LogOffsetMetadata 。

LogOffsetMetadata 类

源码:

/*
 * A log offset structure, including:
 *  1. the message offset
 *  2. the base message offset of the located segment
 *  3. the physical position on the located segment
 */
case class LogOffsetMetadata(messageOffset: Long,
                             segmentBaseOffset: Long = Log.UnknownOffset,
                             relativePositionInSegment: Int = LogOffsetMetadata.UnknownFilePosition) { }

根据源码的定义可以看出,LogOffsetMetadata 有三个重要参数。
1、messageOffset,消息位移,我们所说的高水位的值就是指的这个值。

2、segmentBaseOffset:保存该位移值所在日志段的起始位移。日志段起始位移值辅助计算两条消息在物理磁盘文件中位置的差值,就是两条消息间隔字节数,当然计算的两条消息是在同一日志段上。

3、relativePositionInSegment:保存该位移值所在日志段的磁盘位置。
该类的方法比较简单,主要是用来判断两个位移是否在同一日志段,位移差值等,举例如下:

  // check if this offset is already on an older segment compared with the given offset
  def onOlderSegment(that: LogOffsetMetadata): Boolean = {
    if (messageOffsetOnly)
      throw new KafkaException(s"$this cannot compare its segment info with $that since it only has message offset info")

    this.segmentBaseOffset < that.segmentBaseOffset
  }

onOlderSegment()用来判断给定的位移是否在先前的日志段上。

更新高水位值元数据

这部分的源码可以直接看注释:

  private def updateHighWatermarkMetadata(newHighWatermark: LogOffsetMetadata): Unit = {
    if (newHighWatermark.messageOffset < 0)  //高水位值不能为负数,否则抛异常
      throw new IllegalArgumentException("High watermark offset should be non-negative")

    lock synchronized { //添加监视器锁
      highWatermarkMetadata = newHighWatermark //设置新的HighWatermark
      producerStateManager.onHighWatermarkUpdated(newHighWatermark.messageOffset)  // 处理事务状态管理器的高水位值更新逻辑
      maybeIncrementFirstUnstableOffset()  //处理FirstUnstable 事务
    }
    trace(s"Setting high watermark $newHighWatermark")
  }

更新高水位值

有两种方法updateHighWatermark主要用在 Follower 副本从 Leader 副本获取到消息后更新高水位值。一旦拿到新的消息,就必须要更新高水位值。

// updateHighWatermark method
def updateHighWatermark(hw: Long): Long = {    
// 新高水位值一定介于[Log Start Offset,Log End Offset]之间    
val newHighWatermark = if (hw < logStartOffset)        
	logStartOffset    
	else if (hw > logEndOffset)      
		logEndOffset    
	else  
	hw    
// 更新高水位元数据 
 updateHighWatermarkMetadata(LogOffsetMetadata(newHighWatermark))    
 newHighWatermark  // 最后返回新高水位值  
}

maybeIncrementHighWatermark()主要是用来更新 Leader 的高水位值,leader副本不一定会立即更新高水位的值所以是maybe。

def maybeIncrementHighWatermark(newHighWatermark: LogOffsetMetadata): Option[LogOffsetMetadata] = {
    // 新高水位值不能越过Log End Offset 
    if (newHighWatermark.messageOffset > logEndOffset) throw new IllegalArgumentException(s"High watermark $newHighWatermark " +
      s"update exceeds current " + s"log end offset $logEndOffsetMetadata")
    lock.synchronized {
      // 获取老的高水位值 
      val oldHighWatermark = fetchHighWatermarkMetadata
      // 新高水位值要比老高水位值大以维持单调增加特性,否则就不做更新! 
      // 另外,如果新高水位值在新日志段上,也可执行更新高水位操作 
      if (oldHighWatermark.messageOffset < newHighWatermark.messageOffset || 
        (oldHighWatermark.messageOffset == newHighWatermark.messageOffset && oldHighWatermark.onOlderSegment(newHighWatermark))) {
        updateHighWatermarkMetadata(newHighWatermark)
        Some(oldHighWatermark) // 返回老的高水位值 
      } else {
        None
      }
    }
  }

读取高水位元数据

实现源码如下:


private def fetchHighWatermarkMetadata: LogOffsetMetadata = {
    checkIfMemoryMappedBufferClosed() // 读取时确保日志不能被关闭

    val offsetMetadata = highWatermarkMetadata // 保存当前高水位值到本地变量,避免多线程访问干扰
    if (offsetMetadata.messageOffsetOnly) { //没有获得到完整的高水位元数据
      lock.synchronized {
        val fullOffset = convertToOffsetMetadataOrThrow(highWatermark) // 通过读日志文件的方式把完整的高水位元数据信息解析出来
        updateHighWatermarkMetadata(fullOffset) // 然后再更新一下高水位元数据
        fullOffset
      }
    } else { // 直接返回即可
      offsetMetadata
    }
  }

至此,高水位相关源码就解析完毕,你是否有疑问呢?欢迎留言互相探讨。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值