消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化。
随着kafka的迅猛发展,其消息格式也在不断升级改进,以适应市场的需求。
目前kafka消息格式共经历过三个版本。
1.V0版本
在kafka0.10.0之前都是采用这个版本的消息格式,如下图所示:
每个Record必定有一个offset和message size与其对应。offset用来标记它在分区中的偏移量,这个offset是逻辑值,而非实际物理偏移量。message size表示消息的字节数大小。这两者一起被称为日志头部(LOG_OVERHEAD),固定为12Byte。
每个Record包含以下字段:
(1)crc32:crc32校验值。校验范围是magic到value之间。
(2)magic:消息格式版本号,此版本的magic为0。
(3)attributes:消息的属性,共占用1个字节。低3位表示压缩类型,0表示NONE,1表示GZIP,2表示SNAPPY,3表示LZ4,其余位是保留位。
(4)key length:消息的key长度,如果为-1,表示没有设置key,即key=null。
(5)key:可选的,如果没有key,则无此字段
(6)value length:实际消息体的长度。如果为-1,表示消息为空。
(7)value:消息体。可以为空。
V0版本的一条消息最小长度为crc32+magic+attributes+key length+value length=14Byte,加上head,则占用磁盘空间最少为26Byte。
2.V1版本
kafka从0.10.0版本开始到0.11.0版本之间使用的消息格式版本为V1,比V0版本多了一个timestamp字段,表示消息的时间戳。
V1版本的消息结构如下图所示:
V1的消息格式与V0在以下三个字段上有区别,其他无区别。
(1)magic:消息格式版本号,此版本的magic为1。
(2)timestamp:消息的时间戳。
(3)attributes:消息的属性,共占用1个字节。低3位表示压缩类型,000表示NONE,001表示GZIP,010表示SNAPPY,011表示LZ4。第4位也利用起来了,0表示时间戳类型为CreateTime,1表示时间戳类型为LogAppendTime。其他位保留。
V1版本的一条消息最小长度为crc32+magic+timestamp+attributes+key length+value length=22Byte,加上head,则占用磁盘空间最少为34Byte。
3.V2版本
kafka从0.11.0版本开始所使用的消息格式版本为V2,这个版本对V0和V1有较大的改动,同时参考了Protocol Buffer而引入了变长整型(varint)和ZigZag编码。
3.1.相对V0和V1的改进
在V0和V1中,如果消息本身没有key,那么key length为-1,int类型的需要4个字节来保存,如果采用varints来编码,则需要1个字节。
根据varints的规则,0~63之间的整数占用1个字节,64~8191之间的整数占用2个字节,8192~1048575之间的整数占用3个字节。
如果消息格式中与长度有关的字段采用varints编码,绝大多数情况下都会节省空间,而V2版本的消息格式也正是这样做的。
不过需要注意的是,varints并非一直会节省空间,一个int32最长占用5个字节(大于默认的4字节),一个int64最长占用10字节(大于默认的8字节)。
3.2.V2消息格式
V2版本中消息集称为Record Batch,而不是之前的Message Set,其内部也包含一条或者多条消息。消息格式如下图所示:
每个RecordBatch包含1到多个Record,一个Record中包含0个到多个Header。
3.2.1.RecordBatch attributes字段说明
在BatchRecord格式的attributes字段中,第5位表示此RecordBatch是否处于事务中,1表示事务,0表示非事务。第6位表示是否是Control消息,0表示非Control消息,1表示是Control消息,Control消息用来支持事务功能。
3.2.2.RecordBatch crc32字段说明
对于V1版本的消息,如果用户指定的timestamp类型是LogAppendTime而不是CreateTime,那么消息从Producer进入Broker端之后timestamp字段会被更新,而此值在Producer端已经被计算过一次。再者,Broker端在进行消息格式转换时(比如V1转换成V0)也会重新计算crc的值。在这些类似的情况下,消息从Producer到Consumer之间流动时,crc的值是变动的,需要计算两次crc的值,所以这个字段的设计在V0和V1版本中显得比较鸡肋。在V2版本中,将crc32字段从Record中转移到了RecordBatch中。
在消息压缩场景下,Record Batch Header部分是不被压缩的,而被压缩的只有records字段中的所有内容。
3.3.V2的优势
当消息体越小,V2版本中Record字段的占用会比V1版本的LogHeader+Record占用越小,当达到某个临界点时,可忽略V2版本中Record Batch Header中61Byte大小的影响。
如果消息体很大,V2版本的空间占用也不会比V1版本的空间占用打太多,在几十个Byte以内。
V2版本的消息不仅提供了类似事务、幂等等更多的功能,还对空间占用提供了足够的优化,总体提升很大。