kafka本地存储3-LogSegment

来源:互联网 发布:ecshop多用户商城源码 编辑:程序博客网 时间:2024/06/08 08:23

LogSegment
classLogSegment(vallog: FileMessageSet,
                valindex: OffsetIndex,
                
val baseOffset: Long, //这个Segment存储的消息的partition起始的消息偏移量
                
val indexIntervalBytes: Int,
                
val rollJitterMs: Long,
                 time: Time)

//LogSegment.rollJitterMs字段说明
 
//如果这个segment即将写满,就开始创建新的segment进行回滚,并返回需要写入的segment
   
//segment.size这个LogSegment已经写入的消息占用的字节大小
   
//1.目前的segment空闲字节不足,segment.size > config.segmentSize - messagesSize
   
//2.按照时间间隔进行回滚
   
//3.segment索引文件满,segment.index.isFull
   
//config.segmentMs是段对象存活的时间@param segmentMs
   
//为避免config.segmentMs后segment同时进行回滚,用@param segmentJitterMs 来错开进行回滚
 
//roll,进行回滚操作
 
//1.取出segments.lastEntry的segment,把索引文件缩小到当前存储的索引个数的长度
 
//2.创建LogSegment
 
//3.添加到成员变量this.segments
 
//key为这个Segment存储的消息的partition起始的消息偏移量
 
//value为LogSegment
 
//4.flush
 
//1).把this.recoveryPoint到newOffset的partition的绝对偏移量的segment列表,来逐个flush
 
//segment.flush就是把index和log文件进行flush
 
//2).用参数newOffset来设置this.recoveryPoint
 //3).this.lastflushedTime设置当前时间time.milliseconds


 //LogSegment.created创建这个LogSegment记录的时间戳
 var created = time.milliseconds
 //this.bytesSinceLastIndexEntry记录写入的字节数,如果字节数超过成员indexIntervalBytes,就开始写入到索引中
 private var bytesSinceLastIndexEntry= 0

 //把消息集合写入到log中,如果bytesSinceLastIndexEntry字节超过indexIntervalBytes,
 //就先把下一条的绝对offset和在这个seg的物理字节偏移写入索引中
 def append(offset: Long, messages: ByteBufferMessageSet)
 //从startingFilePosition查找绝对offset的OffsetPosition信息
 
//先从索引中找一定的范围,之后再从log中具体的字节位置
 
//@参数offset 是在partition的绝对消息偏移量
 
//offset是在partition中的消息绝对位移
 
//position是partition中片段segment的物理的字节偏移量
 //case class OffsetPosition(val offset: Long,val position: Int)
 private[log]def translateOffset(offset: Long, startingFilePosition: Int = 0): OffsetPosition 
 //从startOffset开始读,两个maxOffset和maxSize参数为从段中读取数据的上限
 
//读取到偏移量maxOffset,或者读取段中maxSize字节的内容
 
//@参数startOffset 是在partition的绝对消息偏移量
 
//从startOffset转化为字节位置信息
 
//如果绝对maxOffset不为空,就把maxOffset转化为对应的结尾字节偏移位置
 
//如果绝对maxOffset为空,就把maxSize当作结尾字节位置
 //之后返回FetchDataInfo(offsetMetadata, log.read(startPosition.position, length))
 def read(startOffset: Long, maxOffset: Option[Long], maxSize: Int): FetchDataInfo
 //重建索引
 
//把该段存储字节长度调整为参数maxMessageSize,索引也会一起调整
     
//在遍历log.iterator时,如果超过maxMessageSize,就会抛异常
   //在抛异常时读取的位置就是要截取的后一个消息
 //返回truncated = log.sizeInBytes - validBytes
 
//validBytes表示重建索引时成功遍历message时的字节数综合
 //truncated不为0,表示在重建索引过程中出现异常
 def recover(maxMessageSize: Int): Int
 //先截断到offset,之后从参数offset的位置开始写入消息
 def truncateTo(offset: Long): Int
 //得到改seg要写入的下一个位置的partition绝对偏移
 
//取出索引中存储最后一个索引信息的绝对partition消息偏移值,组成FetchDataInfo
 
//从FetchDataInfo.messageSet.lastOption 来得到最后一个消息,
 //MessageAndOffset.nextOffset为下一个要写入的partition的绝对消息偏移
 def nextOffset(): Long


OffsetIndex
每个partition由多个segment组成,每个segment对应一个OffsetIndex

OffsetIndex(@volatilevarfile: File,valbaseOffset: Long,valmaxIndexSize: Int = -1)
baseOffset为该segment在partition上的绝对消息偏移量
maxIndexSize是这个索引文件的最大字节数,索引文件的每个结构都是8字节,需要把文件长度进行8字节对齐

OffsetIndex的每个结构占用8字节
前4个字节为在segment中的消息相对偏移量
后4字节为在这个segment中的物理字节偏移量

OffsetIndex作用
查询给定的partition的绝对消息偏移量在segment的物理字节偏移量时,需要先从OffsetIndex查到大概在segment的物理字节偏移位置
之后在根据这个位置在segment进行向后查找,找到在segment具体的物理字节偏移值
1.targetOffset是partition的绝对偏移量,转化成segment对应的OffsetIndex的相对偏移值,relOffset= targetOffset - OffsetIndex.baseOffset
2.OffsetIndex找到不大于relOffset的8字节结构
3.创建OffsetPosition对象,OffsetPosition(baseOffset+ relativeOffset(idx,slot), physical(idx,slot))
4.通过physical字节偏移,在segment中向后查找,直到找到对应的targetOffset所在的消息为止


Log
//recoveryPoint 是一个消息偏移量,表示要从这个偏移量所在的段segment开始到最新段segment进行索引重建
//如果参数dir的父目录下有.kafka_cleanshutdown后缀的文件,就不进行索引重建
//每次append后this.recoveryPoint = activeSegment.nextOffset
classLog(valdir: File,
          @volatilevarconfig: LogConfig,
          @
volatilevarrecoveryPoint: Long =0L,
          scheduler: Scheduler,
          time: Time = SystemTime)


 //key为这个Segment存储的消息的partition起始的消息偏移量
 //value为LogSegment
  valsegments: ConcurrentNavigableMap[java.lang.Long, LogSegment] =new ConcurrentSkipListMap[java.lang.Long, LogSegment]
  loadSegments()

 //1.读取参数dir中的segment信息,.deleted或.cleaned后缀,直接删除,删除掉index的swap文件,把log的swap文件改名为log文件
 
//2.如果只存在index文件,没有对应的log文件,那删除掉这个index
 
//3.根据文件,创建LogSegment,.log文件的文件名是这个segment的在partition的绝对的消息偏移,如果没有index文件,就重建索引
 
//4.设置到成员变量字典中segments.put(start, segment)
 
//如果目录中没有文件,就segments.put(0L, new LogSegment
 //目录中有文件,取出绝对消息偏移量在this.recoveryPoint到Long.MaxValue的partiton范围的Segment列表
 //对这个列表进行重建索引
 
//如果重建索引有异常,就利用定时器删除异常之后的segment列表
 
//5.遍历segments字典,验证索引的有效性
 
//1).索引记录的lastOffset要大于该segment的baseOffset
 
//2).索引文件长度必须能被8整除
 private def loadSegments()

 //LogOffsetMetadata(messageOffset,segmentBaseOffset,relativePositionInSegment)
 
//@参数messageOffset 是在partition的绝对消息偏移量
  //@参数segmentBaseOffset 是在这个seg中的起始绝对偏移量
  //@relativePositionInSegment 是在seg中的物理字节偏移量
 
 
//activeSegment.nextOffset()解释
 
//得到改seg要写入的下一个位置的partition绝对偏移
 
//取出索引中存储最后一个索引信息的绝对partition消息偏移值,组成FetchDataInfo
 
//从FetchDataInfo.messageSet.lastOption 来得到最后一个消息,
 
//MessageAndOffset.nextOffset为下一个要写入的partition的绝对消息偏移
 var nextOffsetMetadata = new LogOffsetMetadata(activeSegment.nextOffset(), activeSegment.baseOffset, activeSegment.size.toInt)


 //更新成员变量this.nextOffsetMetadata
 
//LogOffsetMetadata(messageOffset, activeSegment.baseOffset, activeSegment.size.toInt)
 
//@参数messageOffset 是在partition的绝对消息偏移量
 
//@参数segmentBaseOffset 是在这个seg中的起始绝对偏移量
 
//@relativePositionInSegment 是在seg中的物理字节偏移量
 
private def updateLogEndOffset(messageOffset: Long) {
   
nextOffsetMetadata =newLogOffsetMetadata(messageOffset, activeSegment.baseOffset, activeSegment.size.toInt)
  }



append
  defappend(messages: ByteBufferMessageSet, assignOffsets: Boolean =true): LogAppendInfo
  //@参数messages,需要添加的集合ByteBufferMessageSet
 //@参数assignOffsets.是否使用参数messages的offset来添加消息,如果不是,就使用log.nextOffsetMetadata.messageOffset递增来添加消息
   //class LogAppendInfo(var firstOffset: Long,var lastOffset: Long, codec: CompressionCodec,
   
//shallowCount: Int, validBytes: Int, offsetsMonotonic: Boolean)
   
//通过分析消息集合ByteBufferMessageSet得到如下信息,1)验证单个消息不能超过config.maxMessageSize,2)验证Crc校验码
   
//之后创建LogAppendInfo,并返回该对象
   
//firstOffset在这个消息集合中的第一个消息的partition绝对偏移量
   
//lastOffset在这个消息集合中的最后一个消息的partition绝对偏移量
   
//codec是这个集合的压缩类型
   
//shallowMessageCount 是集合中消息个数
   
//validBytesCount是集合中消息总字节大小
   
//monotonic 是这个偏移量是否是自增的, false表示offset不是自增的
   //messageSize > config.maxMessageSize


   //LogAppendInfo.validBytes 不等于 messages.sizeInBytes
   //buffer.limit(LogAppendInfo.validBytes)之后用这个buff创建ByteBufferMessageSet

     //参数assignOffsets为true, 重置该set中的消息位置的值 validMessages.assignOffsets(offset,appendInfo.codec)
//如果消息的大小超过config.maxMessageSize,就抛异常MessageSizeTooLargeException
//消息集合的总大小超过配置的config.segmentSize,抛异常MessageSetSizeTooLargeException

     
 //如果这个segment即将写满,就开始创建新的segment进行回滚,并返回需要写入的segment
   
//segment.size这个LogSegment已经写入的消息占用的字节大小
   
//1.目前的segment空闲字节不足,segment.size > config.segmentSize - messagesSize
   
//2.按照时间间隔进行回滚
   
//3.segment索引文件满,segment.index.isFull
   
//config.segmentMs是段对象存活的时间@param segmentMs
   
//为避免config.segmentMs后segment同时进行回滚,用@param segmentJitterMs 来错开进行回滚
 
//roll,进行回滚操作
 
//1.取出segments.lastEntry的segment,把索引文件缩小到当前存储的索引个数的长度
 
//2.创建LogSegment
 
//3.添加到成员变量this.segments
 
//key为这个Segment存储的消息的partition起始的消息偏移量
 
//value为LogSegment
 
//4.flush
 
//1).把this.recoveryPoint到newOffset的partition的绝对偏移量的segment列表,来逐个flush
 
//segment.flush就是把index和log文件进行flush
 
//2).用参数newOffset来设置this.recoveryPoint
 //3).this.lastflushedTime设置当前时间time.milliseconds

       //把消息集合写入到log中,如果bytesSinceLastIndexEntry字节超过indexIntervalBytes,
       //就先把下一条的绝对offset和在这个seg的物理字节偏移写入索引中

       //更新成员变量this.nextOffsetMetadata
       
//LogOffsetMetadata(messageOffset, activeSegment.baseOffset, activeSegment.size.toInt)
       
//@参数messageOffset 是在partition的绝对消息偏移量
       
//@参数segmentBaseOffset 是在这个seg中的起始绝对偏移量
       //@relativePositionInSegment 是在seg中的物理字节偏移量
//nextOffsetMetadata = new LogOffsetMetadata(messageOffset, activeSegment.baseOffset, activeSegment.size.toInt)


           //上次flush到新写入的消息的个数 unflushedMessages() = this.logEndOffset - this.recoveryPoint
       
//unflushedMessages >= config.flushInterval
       
//进行flush时,写入的消息个数的阈值
       
//Log.logEndOffset: Long = nextOffsetMetadata.messageOffset
       
//使用参数nextOffsetMetadata.messageOffset,进行flush
       
//1.把this.recoveryPoint到offset的partition的绝对偏移量的segment列表,来逐个flush
       
//segment.flush就是把index和log文件进行flush
       
//2.用参数offset来设置this.recoveryPoint
       //3.this.lastflushedTime设置当前时间time.milliseconds


read
defread(startOffset: Long, maxLength: Int, maxOffset: Option[Long] = None): FetchDataInfo
     next= nextOffsetMetadata.messageOffset
     //从参数segments得到小于参数startOffset的最大的key值
//Log.segments类型Map[java.lang.Long, LogSegment]
    var entry = segments.floorEntry(startOffset)
     startOffset >next|| entry == null 超过范围,抛异常OffsetOutOfRangeException
   //从startOffset开始读消息集合,两个maxOffset和maxSize参数为从段中读取数据的上限
   //如果读不出来,就从segments向后一个key来继续读,直到读取的信息fetchInfo不为空位置
     valfetchInfo= entry.getValue.read(startOffset, maxOffset, maxLength)
//FetchDataInfo(fetchOffset: LogOffsetMetadata, messageSet: MessageSet)
//LogOffsetMetadata(messageOffset,segmentBaseOffset,relativePositionInSegment)
//@参数messageOffset 是在partition的绝对消息偏移量
//@参数segmentBaseOffset 是在这个seg中的起始绝对偏移量
//@relativePositionInSegment 是在seg中的物理字节偏移量
返回类型FetchDataInfo(fetchOffset: LogOffsetMetadata, messageSet: MessageSet)
0 0
原创粉丝点击