3.1 Kafka
工作流程及文件存储机制
![](https://i-blog.csdnimg.cn/blog_migrate/18f15aab69b761e0c1297842121a2a46.png)
Kafka
中消息是以
topic
进行分类的,生产者生产消息,消费者消费消息,都是面向
topic
的。
topic
是逻辑上的概念,而
partition
是物理上的概念,每个
partition
对应于一个
log
文
件,该
log
文件中存储的就是
producer
生产的数据。
Producer
生产的数据会被不断追加到该
log
文件末端,且每条数据都有自己的
offset
。消费者组中的每个消费者,都会实时记录自己
消费到了哪个
offset
,以便出错恢复时,从上次的位置继续消费。
![](https://i-blog.csdnimg.cn/blog_migrate/8c157d6944366fd99d2670fb1ad936f2.png)
由于生产者生产的消息会不断追加到
log
文件末尾,为防止
log
文件过大导致数据定位
效率低下,
Kafka
采取了
分片
和
索引
机制,将每个
partition
分为多个
segment
。每个
segment
对应两个文件——
“.index”
文件和
“.log”
文件。这些文件位于一个文件夹下,该文件夹的命名
规则为:
topic
名称
+
分区序号。例如,
first
这个
topic
有三个分区,则其对应的文件夹为
first-
0,first-1,first-2
。
![](https://i-blog.csdnimg.cn/blog_migrate/84cc3a18dfa2f3efe8d9e2cc546d2b38.png)
index
和
log
文件以当前
segment
的第一条消息的
offset
命名。下图为
index
文件和
log
文件的结构示意图。
![](https://i-blog.csdnimg.cn/blog_migrate/6c4b834e7d8833c70fd0b57e7609fd23.png)
“
.index
”文件存储大量的索引信息,“
.log
”文件存储大量的数据
,索引文件中的元
数据指向对应数据文件中
message
的物理偏移地址。
3.2 Kafka
生产者
3.2.1
分区策略
1
)分区的原因
(
1
)
方便在集群中扩展
,每个
Partition
可以通过调整以适应它所在的机器,而一个
topic
又可以有多个
Partition
组成,因此整个集群就可以适应任意大小的数据了;
(2)
可以提高并发
,因为可以以
Partition
为单位读写了。
2
)分区的原则
我们需要将
producer
发送的数据封装成一个
ProducerRecord
对象。
![](https://i-blog.csdnimg.cn/blog_migrate/d3084059c9410aedbd8f7d9977b8ad3d.png)
(1)指明 partition 的情况下,直接将指明的值直接作为 partiton 值;
(2)没有指明
partition
值但有
key
的情况下,将
key
的
hash
值与
topic
的
partition
数进行取余得到
partition
值;
(3)既没有
partition
值又没有
key
值的情况下,第一次调用时随机生成一个整数(后
面每次调用在这个整数上自增),将这个值与
topic
可用的
partition
总数取余得到
partition
值,也就是常说的
round-robin
算法。
3.2.2
数据可靠性保证
为保证
producer
发送的数据,能可靠的发送到指定的
topic
,
topic
的每个
partition
收到
producer
发送的数据后,都需要向
producer
发送
ack
(
acknowledgement
确认收到),如果
producer
收到
ack
,就会进行下一轮的发送,否则重新发送数据。
![](https://i-blog.csdnimg.cn/blog_migrate/3b5eab3eca6a3a5bc1c954a4217aa4bf.png)
1
)副本数据同步策略
![](https://i-blog.csdnimg.cn/blog_migrate/aad1bf417f97095275636e2e0c29e35d.png)
Kafka
选择了第二种方案,原因如下:
1.
同样为了容忍
n
台节点的故障,第一种方案需要
2n+1
个副本,而第二种方案只需要
n+1
个副本,而
Kafka
的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
2.
虽然第二种方案的网络延迟会比较高,但网络延迟对
Kafka
的影响较小
2
)
ISR
采用第二种方案之后,设想以下情景:
leader
收到数据,所有
follower
都开始同步数据,
但有一个
follower
,因为某种故障,迟迟不能与
leader
进行同步,那
leader
就要一直等下去,
直到它完成同步,才能发送
ack
。这个问题怎么解决呢?
Leader
维护了一个动态的
in-sync replica set (ISR)
,意为和
leader
保持同步的
follower
集
合。当
ISR
中的
follower
完成数据的同步之后,
leader
就会给
follower
发送
ack
。如果
follower
长时间 未 向
leader
同 步 数 据 , 则 该
follower
将 被 踢 出
ISR
, 该 时 间 阈 值 由
replica.lag.time.max.ms
参数设定。
Leader
发生故障之后,就会从
ISR
中选举新的
leader
。
3
)
ack
应答机制
对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,
所以没必要等
ISR
中的
follower
全部接收成功。
所以
Kafka
为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,
选择以下的配置。
acks
参数配置:
acks
:
0
:
producer
不等待
broker
的
ack
,这一操作提供了一个最低的延迟,
broker
一接收到还
没有写入磁盘就已经返回,当
broker
故障时有可能
丢失数据
;
1
:
producer
等待
broker
的
ack
,
partition
的
leader
落盘成功后返回
ack
,如果在
follower
同步成功之前
leader
故障,那么将会
丢失数据
;
![](https://i-blog.csdnimg.cn/blog_migrate/29f920434da3287b25e7f55d743519dd.png)
-1
(
all
):
producer
等待
broker
的
ack
,
partition
的
leader
和
follower
全部落盘成功后才
返回
ack
。但是如果在
follower
同步完成后,
broker
发送
ack
之前,
leader
发生故障,那么会
造成
数据重复
。
![](https://i-blog.csdnimg.cn/blog_migrate/2f7a49d1635fd5e95965a269ecf73bbc.png)
4)故障处理细节
![](https://i-blog.csdnimg.cn/blog_migrate/d03c61590bc35b7645273b4fb15675da.png)
LEO:指的是每个副本最大的 offset;
HW
:指的是消费者能见到的最大的
offset
,
ISR
队列中最小的
LEO
。
(
1
)
follower
故障
follower
发生故障后会被临时踢出
ISR
,待该
follower
恢复后,
follower
会读取本地磁盘
记录的上次的
HW
,并将
log
文件高于
HW
的部分截取掉,从
HW
开始向
leader
进行同步。
等该
follower
的
LEO
大于等于该
Partition
的
HW
,即
follower
追上
leader
之后,就可以重
新加入
ISR
了。
(
2
)
leader
故障
leader
发生故障之后,会从
ISR
中选出一个新的
leader
,之后,为保证多个副本之间的
数据一致性,其余的 follower 会先将各自的 log 文件
高于
HW
的部分截掉,然后从新的 leader
同步数据。
注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。