这里分析kafka LogSegment源代码

通过一步步分析LogManager,Log源代码之后就会发现,最终的log操作都在LogSegment上实现.LogSegment负责分片的读写恢复刷新删除等动作都在这里实现.LogSegment代码同样在源代码目录log下.

LogSegment是一个日志分片的操作最小单元.直接作用与messages之上.负责实体消息的读写追加等等.

LogSegment实际上是FileMessageSet类的代理类.LogSegment中的所有最终处理都在FileMessageSet类中实现.FileMessageSet类的最终操作建立在ByteBufferMessageSet这个消息实体类的基础上.通过操作FileChannel对象来实现消息读写.

下面来看看主要的一些函数方法.

  初始化部分

class LogSegment(val log: FileMessageSet,     //实际构造是这个.
val index: OffsetIndex,
val baseOffset: Long,
val indexIntervalBytes: Int,
val rollJitterMs: Long,
time: Time) extends Logging { var created = time.milliseconds /* the number of bytes since we last added an entry in the offset index */
private var bytesSinceLastIndexEntry = 0
//在Log中被调用的构造是这个.可以看见是通过topicAndPartition路径和startOffset来创建index和logfile的.
def this(dir: File, startOffset: Long, indexIntervalBytes: Int, maxIndexSize: Int, rollJitterMs: Long, time: Time) =
this(new FileMessageSet(file = Log.logFilename(dir, startOffset)),
new OffsetIndex(file = Log.indexFilename(dir, startOffset), baseOffset = startOffset, maxIndexSize = maxIndexSize),
startOffset,
indexIntervalBytes,
rollJitterMs,
time)

  添加消息函数append

def append(offset: Long, messages: ByteBufferMessageSet) {
if (messages.sizeInBytes > 0) { //判断消息不为空.
trace("Inserting %d bytes at offset %d at position %d".format(messages.sizeInBytes, offset, log.sizeInBytes()))
// append an entry to the index (if needed)
if(bytesSinceLastIndexEntry > indexIntervalBytes) {
index.append(offset, log.sizeInBytes())
this.bytesSinceLastIndexEntry = 0
}
// append the messages
log.append(messages) //调用FileMessageSet类的append方法想写消息.实际上最终调用的是ByteBufferMessageSet类方法来操作消息实体的.
this.bytesSinceLastIndexEntry += messages.sizeInBytes
}
}

  刷新消息到磁盘的flush函数

def flush() {
LogFlushStats.logFlushTimer.time {
log.flush() //可以看见调用的FileMessageSet类的方法.最终FileMessageSet.flush方法调用channel.force方法刷新存储设备.
index.flush() //同上.
}
}

  读取消息的read函数

def read(startOffset: Long, maxOffset: Option[Long], maxSize: Int): FetchDataInfo = {
if(maxSize < 0)
throw new IllegalArgumentException("Invalid max size for log read (%d)".format(maxSize)) val logSize = log.sizeInBytes // this may change, need to save a consistent copy
val startPosition = translateOffset(startOffset) //获取对应offset的读取点位置. // if the start position is already off the end of the log, return null
if(startPosition == null) //没有读取点位置则返回空
return null val offsetMetadata = new LogOffsetMetadata(startOffset, this.baseOffset, startPosition.position) //定义offsetMetadata // if the size is zero, still return a log segment but with zero size
if(maxSize == 0) //最大读取尺寸是0的话.返回空消息.
return FetchDataInfo(offsetMetadata, MessageSet.Empty) // calculate the length of the message set to read based on whether or not they gave us a maxOffset
val length = //计算最大读取的消息总长度.
maxOffset match {
case None => //未设置maxoffset则使用maxsize.
// no max offset, just use the max size they gave unmolested
maxSize
case Some(offset) => { //如果设置了Maxoffset,则计算对应的消息长度.
// there is a max offset, translate it to a file position and use that to calculate the max read size
if(offset < startOffset) //maxoffset小于startoffset则返回异常
throw new IllegalArgumentException("Attempt to read with a maximum offset (%d) less than the start offset (%d).".format(offset, startOffset))
val mapping = translateOffset(offset, startPosition.position) //获取相对maxoffset读取点.
val endPosition =
if(mapping == null)
logSize // the max offset is off the end of the log, use the end of the file
else
mapping.position
min(endPosition - startPosition.position, maxSize) //用maxoffset读取点减去开始的读取点.获取需要读取的数据长度.如果长度比maxsize大则返回maxsize
}
}
FetchDataInfo(offsetMetadata, log.read(startPosition.position, length)) //使用FileMessageSet.read读取相应长度的数据返回FetchDataInfo的封装对象.
}

  读取函数通过映射offset到读取长度.来读取多个offset.

private[log] def translateOffset(offset: Long, startingFilePosition: Int = 0): OffsetPosition = {  //用来将offset映射到读取指针位置的函数.
val mapping = index.lookup(offset) //通过查找index获取对应的指针对象.
log.searchFor(offset, max(mapping.position, startingFilePosition)) //通过FileMessageSet获取对应的指针位置.
}

  recover函数.kafka启动检查时用到的各层调用的最后代理函数.

def recover(maxMessageSize: Int): Int = {
index.truncate()
index.resize(index.maxIndexSize)
var validBytes = 0
var lastIndexEntry = 0
val iter = log.iterator(maxMessageSize)
try {
while(iter.hasNext) {
val entry = iter.next
entry.message.ensureValid()
if(validBytes - lastIndexEntry > indexIntervalBytes) {
// we need to decompress the message, if required, to get the offset of the first uncompressed message
val startOffset =
entry.message.compressionCodec match {
case NoCompressionCodec =>
entry.offset
case _ =>
ByteBufferMessageSet.decompress(entry.message).head.offset
}
index.append(startOffset, validBytes)
lastIndexEntry = validBytes
}
validBytes += MessageSet.entrySize(entry.message)
}
} catch {
case e: InvalidMessageException =>
logger.warn("Found invalid messages in log segment %s at byte offset %d: %s.".format(log.file.getAbsolutePath, validBytes, e.getMessage))
}
val truncated = log.sizeInBytes - validBytes
log.truncateTo(validBytes)
index.trimToValidSize()
truncated
}

  分片删除函数

 def delete() {
val deletedLog = log.delete() //最终是删除文件,关闭内存数组.在FileMessageSet里实现.
val deletedIndex = index.delete() //同上.
if(!deletedLog && log.file.exists)
throw new KafkaStorageException("Delete of log " + log.file.getName + " failed.")
if(!deletedIndex && index.file.exists)
throw new KafkaStorageException("Delete of index " + index.file.getName + " failed.")
}

到这里LogSegment主要函数都分析完了.

Kafka 源代码分析之LogSegment的更多相关文章

  1. Kafka 源代码分析之LogManager

    这里分析kafka 0.8.2的LogManager logmanager是kafka用来管理log文件的子系统.源代码文件在log目录下. 这里会逐步分析logmanager的源代码.首先看clas ...

  2. Kafka 源代码分析.

    这里记录kafka源代码笔记.(代码版本是0.8.2.1) kafka的源代码如何下载.这里简单说一下. git clone https://git-wip-us.apache.org/repos/a ...

  3. Kafka 源代码分析之FileMessageSet

    这里主要分析FileMessageSet类 这个类主要是管理log消息的内存对象和文件对象的类.源代码文件在log目录下.这个类被LogSegment类代理调用用来管理分片. 下面是完整代码.代码比较 ...

  4. Kafka 源代码分析之ByteBufferMessageSet

    这里分析一下message的封装类ByteBufferMessageSet类 ByteBufferMessageSet类的源代码在源代码目录message目录下.这个类主要封装了message,mes ...

  5. Kafka 源代码分析之Log

    这里分析Log对象本身的源代码. Log类是一个topic分区的基础类.一个topic分区的所有基本管理动作.都在这个对象里完成.类源代码文件为Log.scala.在源代码log目录下. Log类是L ...

  6. kafka 源代码分析之Message(v0.10)

    这里主要更新一下kafka 0.10.0版本的message消息格式的变化. message 的格式在0.10.0的版本里发生了一些变化(相对于0.8.2.1的版本)这里把0.10.0的message ...

  7. Kafka 源代码分析之Message

    这里主要分析一下message的格式. 一条message的构成由以下部分组成 val CrcOffset = 0 //crc校验部分和字长 val CrcLength = 4 val MagicOf ...

  8. Kafka 源代码分析之log框架介绍

    这里主要介绍log管理,读写相关的类的调用关系的介绍. 在围绕log的实际处理上.有很多层的封装和调用.这里主要介绍一下调用结构和顺序. 首先从LogManager开始. 调用关系简单如下:LogMa ...

  9. Kafka 源代码分析之MessageSet

    这里分析MessageSet类 MessageSet是一个抽象类,定义了一条log的一些接口和常量,FileMessageSet就是MessageSet类的实现类.一条日志中存储的log完整格式如下 ...

随机推荐

  1. Git详细教程---多人协作开发

    Git可以完成两件事情: 1. 版本控制 2.多人协作开发 如今的项目,规模越来越大,功能越来越多,需要有一个团队进行开发. 如果有多个开发人员共同开发一个项目,如何进行协作的呢. Git提供了一个非 ...

  2. [刷题]算法竞赛入门经典(第2版) 5-7/UVa12100 - Printer Queue

    题意:一堆文件但只有一个打印机,按优先级与排队顺序进行打印.也就是在一个可以插队的的队列里,问你何时可以打印到.至于这个插队啊,题目说"Of course, those annoying t ...

  3. 在Windows上安装MongoDB

    原文官方文档:https://docs.mongodb.org/v2.6/tutorial/install-mongodb-on-windows/ 基于版本:MongoDB 2.6 概览 通过这个示例 ...

  4. 点击文字选中checkbox

    <html> <head> <title>test</title> <meta http-equiv="content-Type&quo ...

  5. 《安卓网络编程》之第四篇 处理URL地址

    在Android手机系统中,可以通过URL地址获取网络资源.在URL类的众多方法中,可以使用openStream()方法来读取该URL资源的输入流InputStream.在此方法的基础上可以引申出很多 ...

  6. git使用简易指南

    安装 下载 git OSX 版 下载 git Windows 版 下载 git Linux 版 创建新仓库 创建新文件夹,打开,然后执行 git init 以创建新的 git 仓库. 检出仓库 执行如 ...

  7. 利用R语言进行交互数据可视化(转)

    上周在中国R语言大会北京会场上,给大家分享了如何利用R语言交互数据可视化.现场同学对这块内容颇有兴趣,故今天把一些常用的交互可视化的R包搬出来与大家分享. rCharts包 说起R语言的交互包,第一个 ...

  8. Virtualbox让kali虚拟机共享主机的无线网络连接

    今天在测试虚拟机下安装kali系统时,遇到一个问题,默认安装完kali系统后,虚拟机不能上网.虚拟机网络配置使用的是默认的网络地址转换(NAT)选项. 网上查了很多,都说使用NAT模式时虚拟机不用做任 ...

  9. iOS安全攻防之越狱设备检测

    iOS 越狱(iOS Jailbreaking),是用于获取苹果公司便携装置操作系统iOS最高权限的一种技术手段,用户使用这种技术及软件可以获取到 iOS 的最高权限,甚至可能可以进一步解开运营商对手 ...

  10. Spring Boot 负载均衡之外置session状态保存

    在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而打到另外一台服务器的时候,session丢失. 常规的解决方 ...