Kafka 源代码分析之ByteBufferMessageSet
这里分析一下message的封装类ByteBufferMessageSet类
ByteBufferMessageSet类的源代码在源代码目录message目录下.这个类主要封装了message,messageset,messageandoffset等类的对象.在Log类中读写log的时候基本上都是以这个类的对象为基本操作对象的.
下面看看类的具体代码.首先是初始化部分.
class ByteBufferMessageSet(val buffer: ByteBuffer) extends MessageSet with Logging { //这里作为入口传入ByteBuffer类型的buffer
private var shallowValidByteCount = -1 //设定了全局的验证字节统计变量.
//这里是几个不同的构造函数.但是都是以最初的构造函数为基础的.这里调用了BBMS对象的create方法创建新buffer.
def this(compressionCodec: CompressionCodec, messages: Message*) { //参数是压缩标志和message
this(ByteBufferMessageSet.create(new AtomicLong(0), compressionCodec, messages:_*))
}
//同上.只是参数不同而已.
def this(compressionCodec: CompressionCodec, offsetCounter: AtomicLong, messages: Message*) {
this(ByteBufferMessageSet.create(offsetCounter, compressionCodec, messages:_*))
}
//最简单的消息构造函数.
def this(messages: Message*) {
this(NoCompressionCodec, new AtomicLong(0), messages: _*)
}
上面是几个不同的构造函数部分.构造函数应用到的ByteBufferMessageSet.create函数代码如下.
object ByteBufferMessageSet {
//创建函数的参数是offset,压缩,message.返回的是bytebuffer类型.
private def create(offsetCounter: AtomicLong, compressionCodec: CompressionCodec, messages: Message*): ByteBuffer = {
if(messages.size == 0) { //消息为空的话就返回一个空的buffer
MessageSet.Empty.buffer
} else if(compressionCodec == NoCompressionCodec) { //没有压缩的话.就分配一个新的messages大小的buffer.
val buffer = ByteBuffer.allocate(MessageSet.messageSetSize(messages))
for(message <- messages)
writeMessage(buffer, message, offsetCounter.getAndIncrement) //在这里将消息内容写入buffer中.
buffer.rewind() //将指针设置到开始位置.
buffer //返回buffer
} else { //这里则是启用压缩的处理动作了.
val byteArrayStream = new ByteArrayOutputStream(MessageSet.messageSetSize(messages)) //获取一个messages大小的字节流对象
val output = new DataOutputStream(CompressionFactory(compressionCodec, byteArrayStream)) //创建写入对象.
var offset = -1L
try {
for(message <- messages) { //将消息压缩写入字节流对象中去.
offset = offsetCounter.getAndIncrement
output.writeLong(offset)
output.writeInt(message.size)
output.write(message.buffer.array, message.buffer.arrayOffset, message.buffer.limit)
}
} finally {
output.close()
}
val bytes = byteArrayStream.toByteArray //转成字节
val message = new Message(bytes, compressionCodec) //创建一个压缩的消息对象.
val buffer = ByteBuffer.allocate(message.size + MessageSet.LogOverhead) /分配一个buffer
writeMessage(buffer, message, offset) //将压缩过的message写入到buffer.
buffer.rewind()
buffer
}
}
def decompress(message: Message): ByteBufferMessageSet = {
val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream
val inputStream: InputStream = new ByteBufferBackedInputStream(message.payload)
val intermediateBuffer = new Array[Byte](1024)
val compressed = CompressionFactory(message.compressionCodec, inputStream)
try {
Stream.continually(compressed.read(intermediateBuffer)).takeWhile(_ > 0).foreach { dataRead =>
outputStream.write(intermediateBuffer, 0, dataRead)
}
} finally {
compressed.close()
}
val outputBuffer = ByteBuffer.allocate(outputStream.size)
outputBuffer.put(outputStream.toByteArray)
outputBuffer.rewind
new ByteBufferMessageSet(outputBuffer)
}
//将message写入buffer
private[kafka] def writeMessage(buffer: ByteBuffer, message: Message, offset: Long) {
buffer.putLong(offset) //先写入offset 之后是size,最后是消息本身.
buffer.putInt(message.size)
buffer.put(message.buffer)
message.buffer.rewind()
}
}
上面就是关于初始化部分做的工作.下面看看具体的函数.这个函数是作为核心函数.messages映射成对应的messageandoffset就在这里实现.被外部调用的好几个函数也都是依赖这个函数实现.
override def iterator: Iterator[MessageAndOffset] = internalIterator() /** iterator over compressed messages without decompressing */
def shallowIterator: Iterator[MessageAndOffset] = internalIterator(true) /** When flag isShallow is set to be true, we do a shallow iteration: just traverse the first level of messages. **/
private def internalIterator(isShallow: Boolean = false): Iterator[MessageAndOffset] = { //可以看见返回的是一个迭代对象,对象类想是messageandoffset
new IteratorTemplate[MessageAndOffset] { //这是一个抽象类.在这个地方返回了一个匿名类的实例.
var topIter = buffer.slice() //复制一份buffer
var innerIter: Iterator[MessageAndOffset] = null def innerDone():Boolean = (innerIter == null || !innerIter.hasNext) def makeNextOuter: MessageAndOffset = { //这里主要把没个message映射成messageandoffset
// if there isn't at least an offset and size, we are done
if (topIter.remaining < 12)
return allDone()
val offset = topIter.getLong()
val size = topIter.getInt()
if(size < Message.MinHeaderSize)
throw new InvalidMessageException("Message found with corrupt size (" + size + ")") // we have an incomplete message
if(topIter.remaining < size)
return allDone()
//上面这些主要是检查啥的.不细说了.主要映射在下面完成.
// read the current message and check correctness
val message = topIter.slice()
message.limit(size) //通过size截取第一个message
topIter.position(topIter.position + size) //将指针往前移.
val newMessage = new Message(message) //构建新message if(isShallow) {
new MessageAndOffset(newMessage, offset) //做映射返回.
} else { //剩下是判断是否有压缩的情况.
newMessage.compressionCodec match {
case NoCompressionCodec =>
innerIter = null
new MessageAndOffset(newMessage, offset)
case _ =>
innerIter = ByteBufferMessageSet.decompress(newMessage).internalIterator() //有压缩用对象里的方法处理.
if(!innerIter.hasNext)
innerIter = null
makeNext()
}
}
}
//在这个函数里调用映射函数.最后这个函数有next函数调用.
override def makeNext(): MessageAndOffset = {
if(isShallow){
makeNextOuter
} else {
if(innerDone())
makeNextOuter
else
innerIter.next
}
} }
}
以上就是一个核心函数的处理过程.看看对应的抽象类IteratorTemplate的定义.
abstract class IteratorTemplate[T] extends Iterator[T] with java.util.Iterator[T] {
private var state: State = NOT_READY
private var nextItem = null.asInstanceOf[T]
def next(): T = {
if(!hasNext())
throw new NoSuchElementException()
state = NOT_READY
if(nextItem == null)
throw new IllegalStateException("Expected item but none found.")
nextItem
}
def peek(): T = {
if(!hasNext())
throw new NoSuchElementException()
nextItem
}
def hasNext(): Boolean = {
if(state == FAILED)
throw new IllegalStateException("Iterator is in failed state")
state match {
case DONE => false
case READY => true
case _ => maybeComputeNext()
}
}
protected def makeNext(): T
def maybeComputeNext(): Boolean = {
state = FAILED
nextItem = makeNext()
if(state == DONE) {
false
} else {
state = READY
true
}
}
protected def allDone(): T = {
state = DONE
null.asInstanceOf[T]
}
def remove =
throw new UnsupportedOperationException("Removal not supported")
protected def resetState() {
state = NOT_READY
}
}
定义很简单直白.
这个类主要封装了message和messageandoffset的操作对象.在Log,LogSegment中被用来操作消息实例.
Kafka 源代码分析之ByteBufferMessageSet的更多相关文章
- Kafka 源代码分析之LogManager
这里分析kafka 0.8.2的LogManager logmanager是kafka用来管理log文件的子系统.源代码文件在log目录下. 这里会逐步分析logmanager的源代码.首先看clas ...
- Kafka 源代码分析.
这里记录kafka源代码笔记.(代码版本是0.8.2.1) kafka的源代码如何下载.这里简单说一下. git clone https://git-wip-us.apache.org/repos/a ...
- Kafka 源代码分析之FileMessageSet
这里主要分析FileMessageSet类 这个类主要是管理log消息的内存对象和文件对象的类.源代码文件在log目录下.这个类被LogSegment类代理调用用来管理分片. 下面是完整代码.代码比较 ...
- Kafka 源代码分析之LogSegment
这里分析kafka LogSegment源代码 通过一步步分析LogManager,Log源代码之后就会发现,最终的log操作都在LogSegment上实现.LogSegment负责分片的读写恢复刷新 ...
- Kafka 源代码分析之Log
这里分析Log对象本身的源代码. Log类是一个topic分区的基础类.一个topic分区的所有基本管理动作.都在这个对象里完成.类源代码文件为Log.scala.在源代码log目录下. Log类是L ...
- kafka 源代码分析之Message(v0.10)
这里主要更新一下kafka 0.10.0版本的message消息格式的变化. message 的格式在0.10.0的版本里发生了一些变化(相对于0.8.2.1的版本)这里把0.10.0的message ...
- Kafka 源代码分析之Message
这里主要分析一下message的格式. 一条message的构成由以下部分组成 val CrcOffset = 0 //crc校验部分和字长 val CrcLength = 4 val MagicOf ...
- Kafka 源代码分析之MessageSet
这里分析MessageSet类 MessageSet是一个抽象类,定义了一条log的一些接口和常量,FileMessageSet就是MessageSet类的实现类.一条日志中存储的log完整格式如下 ...
- Kafka 源代码分析之log框架介绍
这里主要介绍log管理,读写相关的类的调用关系的介绍. 在围绕log的实际处理上.有很多层的封装和调用.这里主要介绍一下调用结构和顺序. 首先从LogManager开始. 调用关系简单如下:LogMa ...
随机推荐
- Maven学习-优化和重构POM
在一个复杂的项目中,项目的各个模块存在各种相互依赖关系.优化一个多模块项目的POM最好通过几步来做.总的来说,我们总是寻找一个POM中的重复或者多个兄弟POM中的重复.在多模块项目中依赖重复的模式主要 ...
- jQuery手风琴菜单!!!!
jQuery手风琴菜单 第一次发博客也不知道说点什么好,以前敲得一个手风琴菜单刚刚整理出来了,就来分享个大家 手风琴的排版 排版完事了,接下来就写样式吧,把自己喜欢的颜色或者是图片添加进来,就会变成你 ...
- 深入理解Struts2----类型转换
之前的一系列文章主要介绍了有关Struts2的一些基本用法和部分的简单原理,但是始终没有介绍有关拦截器的相关内容,从本篇开始我们将从另一个角度去深入理解框架的使用,核心还是拦截器,但本篇首先 ...
- 【JAVAWEB学习笔记】19_事务
事务 学习目标 案例-完成转账 一.事务概述 1.什么是事务 一件事情有n个组成单元 要不这n个组成单元同时成功 要不n个单元就同时失败 就是将n个组成单元放到一个事务中 2.mysql的事务 默认的 ...
- numpy之索引和切片
索引和切片 一维数组 一维数组很简单,基本和列表一致. 它们的区别在于数组切片是原始数组视图(这就意味着,如果做任何修改,原始都会跟着更改). 这也意味着,如果不想更改原始数组,我们需要进行显式的复制 ...
- Java 脚本化编程指南
Java 脚本化编程指南 Java脚本化API为谁准备? 脚本语言的一些有用的特性是: 方便:大多数脚本语言都是动态类型的.您通常可以创建新的变量,而不声明变量类型,并且您可以重用变量来存储不同类型的 ...
- Day4-软件目录开发规范
层次清晰的目录结构:1. 可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等.从而非常快速的了解这个项目.2. 可维护性高: 定义好 ...
- html学习笔记 - table表格
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 用户输入与while循环
函数input()的工作原理: 函数input()让程序短暂运行,等待用户输入一些文本,获取用户输入后将其存储在一个变量中 测试input()功能-- #!/usr/bin/env python#fi ...
- pycharm5工具免费分享及安装教程
好东西,就要分享,最近在捣鼓Python,所以就找个pycharm5工具,感觉挺好用的. 废话不多说了,所见即所得: 百度云盘分享:http://pan.baidu.com/s/1sk9k4Nj 密码 ...