架构师之路-如何建立高可用消息中间件kafka?( 二 )


架构师之路-如何建立高可用消息中间件kafka?

文章插图
在Kafka文件存储中 。同一个topic下有多个不同的partition 。每个partiton为一个目录 。partition的名称规则为:topic名称+有序序号 。第一个序号从0开始计 。最大的序号为partition数量减1 。partition是实际物理上的概念 。而topic是逻辑上的概念 。
上面提到partition还可以细分为segment 。这个segment又是什么?如果就以partition为最小存储单位 。我们可以想象当Kafka producer不断发送消息 。必然会引起partition文件的无限扩张 。这样对于消息文件的维护以及已经被消费的消息的清理带来严重的影响 。所以这里以segment为单位又将partition细分 。每个partition(目录)相当于一个巨型文件被平均分配到多个大小相等的segment(段)数据文件中(每个segment 文件中消息数量不一定相等)这种特性也方便old segment的删除 。即方便已被消费的消息的清理 。提高磁盘的利用率 。每个partition只需要支持顺序读写就行 。segment的文件生命周期由服务端配置参数(log.segment.bytes 。log.roll.{ms,hours}等若干参数)决定 。
segment文件由两部分组成 。分别为“.index”文件和“.log”文件 。分别表示为segment索引文件和数据文件 。这两个文件的命令规则为:partition全局的第一个segment从0开始 。后续每个segment文件名为上一个segment文件最后一条消息的offset值 。数值大小为64位 。20位数字字符长度 。没有数字用0填充 。如下:
架构师之路-如何建立高可用消息中间件kafka?

文章插图
以上面的segment文件为例 。展示出segment:00000000000000170410的“.index”文件和“.log”文件的对应的关系 。如下图:
架构师之路-如何建立高可用消息中间件kafka?

文章插图
如上图 。“.index”索引文件存储大量的元数据 。“.log”数据文件存储大量的消息 。索引文件中的元数据指向对应数据文件中message的物理偏移地址 。其中以“.index”索引文件中的元数据[3, 348]为例 。在“.log”数据文件表示第3个消息 。即在全局partition中表示170410+3=170413个消息 。该消息的物理偏移地址为348 。
那么如何从partition中通过offset查找message呢?以上图为例 。读取offset=170418的消息 。首先查找segment文件 。其中00000000000000000000.index为最开始的文件 。第二个文件为00000000000000170410.index(起始偏移为170410+1=170411) 。而第三个文件为00000000000000239430.index(起始偏移为239430+1=239431) 。所以这个offset=170418就落到了第二个文件之中 。其他后续文件可以依次类推 。以其实偏移量命名并排列这些文件 。然后根据二分查找法就可以快速定位到具体文件位置 。其次根据00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置进行读取 。
要是读取offset=170418的消息 。从00000000000000170410.log文件中的1325的位置进行读取 。那么怎么知道何时读完本条消息 。否则就读到下一条消息的内容了?这个就需要联系到消息的物理结构了 。消息都具有固定的物理结构 。包括:offset(8 Bytes)、消息体的大小(4 Bytes)、crc32(4 Bytes)、magic(1 Byte)、attributes(1 Byte)、key length(4 Bytes)、key(K Bytes)、payload(N Bytes)等等字段 。可以确定一条消息的大小 。即读取到哪里截止 。
3.2 复制原理和同步方式
Kafka中topic的每个partition有一个预写式的日志文件 。虽然partition可以继续细分为若干个segment文件 。但是对于上层应用来说可以将partition看成最小的存储单元(一个有多个segment文件拼接的“巨型”文件) 。每个partition都由一些列有序的、不可变的消息组成 。这些消息被连续的追加到partition中 。
上图中有两个新名词:HW和LEO 。这里先介绍下LEO 。LogEndOffset的缩写 。表示每个partition的log最后一条Message的位置 。HW是HighWatermark的缩写 。是指consumer能够看到的此partition的位置 。这个涉及到多副本的概念 。这里先提及一下 。下节再详表 。
言归正传 。为了提高消息的可靠性 。Kafka每个topic的partition有N个副本(replicas) 。其中N(大于等于1)是topic的复制因子(replica fator)的个数 。Kafka通过多副本机制实现故障自动转移 。当Kafka集群中一个broker失效情况下仍然保证服务可用 。在Kafka中发生复制时确保partition的日志能有序地写到其他节点上 。N个replicas中 。其中一个replica为leader 。其他都为follower, leader处理partition的所有读写请求 。与此同时 。follower会被动定期地去复制leader上的数据 。
如下图所示 。Kafka集群中有4个broker, 某topic有3个partition,且复制因子即副本个数也为3:

推荐阅读