Kafka.03 - Message 介绍
消息简介
消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和 CRC32 校验码。
具有 N 个字节的消息的格式如下:
如果版本号是 0:
1 个字节的 "magic" 标记
4 个字节的 CRC32 校验码
N - 5 个字节的具体信息
如果版本号是 1:
1 个字节的 "magic" 标记
1 个字节的参数允许标注一些附加的信息比如是否压缩了,解码类型等
4 个字节的 CRC32 校验码
N - 6 个字节的具体信息
日志简介
日志是一个叫做my_topic
且有两个分区的的 topic,它的日志有两个文件夹组成,my_topic_0
和my_topic_1
,每个文件夹里放着具体的数据文件,每个数据文件都是一系列的日志实体,每个日志实体有一个 4 个字节的整数 N 标注消息的长度,后边跟着 N 个字节的消息。
每个消息都可以由一个 64 位的整数 offset 标注,offset 标注了这条消息在发送到这个分区的消息流中的起始位置。每个日志文件的名称都是这个文件第一条日志的 offset.所以第一个日志文件的名字就是 00000000000.kafka。所以每相邻的两个文件名字的差就是一个数字 S,S 差不多就是配置文件中指定的日志文件的最大容量。
消息的实现
消息的格式都由一个统一的接口维护,所以消息可以在 producer,broker 和 consumer 之间无缝的传递。存储在硬盘上的消息格式如下所示:
消息长度: 4 bytes (value: 1+4+n)
版本号: 1 byte
CRC 校验码: 4 bytes
具体的消息: n bytes
写操作(Appends)
写操作本质上就是将消息不断的追加到最后一个日志的末尾,当日志的大小达到一个指定的值时就会产生一个新的文件。
对于写操作有两个参数,一个规定了消息的数量达到这个值时必须将数据刷新到硬盘上,另外一个规定了刷新到硬盘的时间间隔,这对数据的持久性是个保证,在系统崩溃的时候只会丢失一定数量的消息或者一个时间段的消息。
读操作(Reads)
读操作需要两个参数:一个 64 位的 offset 和一个 S 字节的最大读取量。S 通常比单个消息的大小要大,但在一些个别消息比较大的情况下,S 会小于单个消息的大小。这种情况下读操作会不断重试,每次重试都会将读取量加倍,直到读取到一个完整的消息。可以配置单个消息的最大值,这样服务器就会拒绝大小超过这个值的消息。也可以给客户端指定一个尝试读取的最大上限,避免为了读到一个完整的消息而无限次的重试。
在实际执行读取操纵时,首先需要定位数据所在的日志文件,然后根据 offset 计算出在这个日志中的 offset(前面的的 offset 是整个分区的 offset),然后在这个 offset 的位置进行读取。定位操作是由二分查找法完成的,Kafka 在内存中为每个文件维护了 offset 的范围。
删除(Deletes)
日志管理器允许定制删除策略。目前的策略是删除修改时间在 N 天之前的日志(按时间删除),也可以使用另外一个策略:保留最后的 N GB 数据的策略(按大小删除)。
为了避免在删除时阻塞读操作,Kafka 采用了 copy-on-write 形式的实现,当删除操作进行时,读取操作的二分查找功能实际是在一个静态的快照副本上进行的,这类似于 Java 的 CopyOnWriteArrayList。
可靠性保证
日志文件有一个可配置的参数 M,缓存超过这个数量的消息将被强行刷新到硬盘。
一个日志矫正线程将循环检查最新的日志文件中的消息确认每个消息都是合法的。
合法的标准为:所有文件的大小的和最大的 offset 小于日志文件的大小,并且消息的 CRC32 校验码与存储在消息实体中的校验码一致。
如果在某个 offset 发现不合法的消息,从这个 offset 到下一个合法的 offset 之间的内容将被移除。
有两种情况会发生写入消息不合法:
当发生崩溃时有些数据块未能写入。
写入了一些空白数据块。第二种情况的原因是,对于每个文件,操作系统都有一个 inode(inode 是指在许多“类 Unix 文件系统”中的一种数据结构。每个 inode 保存了文件系统中的一个文件系统对象,包括文件、目录、大小、设备文件、socket、管道, 等等),但无法保证更新 inode 和写入数据的顺序,当 inode 保存的大小信息被更新了,但写入数据时发生了崩溃,就产生了空白数据块。CRC 校验码可以检查这些块并移除,当然因为崩溃而未写入的数据块也就丢失了。
评论