《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 www.cnblogs.com/itrena/p/8986984.html 「挨踢人啊」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一、kafka的存储机制

kafka通过topic来分主题存放数据,主题内有分区,分区可以有多个副本,分区的内部还细分为若干个segment。

所谓的分区其实就是在kafka对应存储目录下创建的文件夹,文件夹的名字是主题名加上分区编号,编号从0开始。

1、segment

所谓的segment其实就是在分区对应的文件夹下产生的文件。

一个分区会被划分成大小相等的若干segment,这样一方面保证了分区的数据被划分到多个文件中保证不会产生体积过大的文件;另一方面可以基于这些segment文件进行历史数据的删除,提高效率。

一个segment又由一个.log和一个.index文件组成。

1..log

.log文件为数据文件用来存放数据分段数据。

2..index

.index为索引文件保存对对应的.log文件的索引信息。

在.index文件中,保存了对对应.log文件的索引信息,通过查找.index文件可以获知每个存储在当前segment中的offset在.log文件中的开始位置,而每条日志有其固定格式,保存了包括offset编号、日志长度、key的长度等相关信息,通过这个固定格式中的数据可以确定出当前offset的结束位置,从而对数据进行读取。

3.命名规则

这两个文件的命名规则为:

partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充。

2、读取数据

开始读取指定分区中某个offset对应的数据时,先根据offset和当前分区的所有segment的名称做比较,确定出数据在哪个segment中,再查找该segment的索引文件,确定当前offset在数据文件中的开始位置,最后从该位置开始读取数据文件,在根据数据格式判断结果,获取完整数据。

二、可靠性保证

1、AR

在Kafka中维护了一个AR列表,包括所有的分区的副本。AR又分为ISR和OSR。

AR = ISR + OSR。

AR、ISR、OSR、LEO、HW这些信息都被保存在Zookeeper中。

1.ISR

ISR中的副本都要同步leader中的数据,只有都同步完成了数据才认为是成功提交了,成功提交之后才能供外界访问。

在这个同步的过程中,数据即使已经写入也不能被外界访问,这个过程是通过LEO-HW机制来实现的。

2.OSR

OSR内的副本是否同步了leader的数据,不影响数据的提交,OSR内的follower尽力的去同步leader,可能数据版本会落后。

最开始所有的副本都在ISR中,在kafka工作的过程中,如果某个副本同步速度慢于replica.lag.time.max.ms指定的阈值,则被踢出ISR存入OSR,如果后续速度恢复可以回到ISR中。

3.LEO

LogEndOffset:分区的最新的数据的offset,当数据写入leader后,LEO就立即执行该最新数据。相当于最新数据标识位。

4.HW

HighWatermark:只有写入的数据被同步到所有的ISR中的副本后,数据才认为已提交,HW更新到该位置,HW之前的数据才可以被消费者访问,保证没有同步完成的数据不会被消费者访问到。相当于所有副本同步数据标识位。

在leader宕机后,只能从ISR列表中选取新的leader,无论ISR中哪个副本被选为新的leader,它都知道HW之前的数据,可以保证在切换了leader后,消费者可以继续看到HW之前已经提交的数据。

所以LEO代表已经写入的最新数据位置,而HW表示已经同步完成的数据,只有HW之前的数据才能被外界访问。

5.HW截断机制

如果leader宕机,选出了新的leader,而新的leader并不能保证已经完全同步了之前leader的所有数据,只能保证HW之前的数据是同步过的,此时所有的follower都要将数据截断到HW的位置,再和新的leader同步数据,来保证数据一致。

当宕机的leader恢复,发现新的leader中的数据和自己持有的数据不一致,此时宕机的leader会将自己的数据截断到宕机之前的hw位置,然后同步新leader的数据。宕机的leader活过来也像follower一样同步数据,来保证数据的一致性。

2、生产者可靠性级别

通过以上的讲解,已经可以保证kafka集群内部的可靠性,但是在生产者向kafka集群发送时,数据经过网络传输,也是不可靠的,可能因为网络延迟、闪断等原因造成数据的丢失。

kafka为生产者提供了如下的三种可靠性级别,通过不同策略保证不同的可靠性保障。

其实此策略配置的就是leader将成功接收消息信息响应给客户端的时机。

通过request.required.acks参数配置:

1:生产者发送数据给leader,leader收到数据后发送成功信息,生产者收到后认为发送数据成功,如果一直收不到成功消息,则生产者认为发送数据失败会自动重发数据。

当leader宕机时,可能丢失数据。

0:生产者不停向leader发送数据,而不需要leader反馈成功消息。

这种模式效率最高,可靠性最低。可能在发送过程中丢失数据,也可能在leader宕机时丢失数据。

-1:生产者发送数据给leader,leader收到数据后要等到ISR列表中的所有副本都同步数据完成后,才向生产者发送成功消息,如果一只收不到成功消息,则认为发送数据失败会自动重发数据。

这种模式下可靠性很高,但是当ISR列表中只剩下leader时,当leader宕机让然有可能丢数据。

此时可以配置min.insync.replicas指定要求观察ISR中至少要有指定数量的副本,默认该值为1,需要改为大于等于2的值

这样当生产者发送数据给leader但是发现ISR中只有leader自己时,会收到异常表明数据写入失败,此时无法写入数据,保证了数据绝对不丢。

虽然不丢但是可能会产生冗余数据,例如生产者发送数据给leader,leader同步数据给ISR中的follower,同步到一半leader宕机,此时选出新的leader,可能具有部分此次提交的数据,而生产者收到失败消息重发数据,新的leader接受数据则数据重复了。

3、leader选举

当leader宕机时会选择ISR中的一个follower成为新的leader,如果ISR中的所有副本都宕机,怎么办?

有如下配置可以解决此问题:

unclean.leader.election.enable=false

策略1:必须等待ISR列表中的副本活过来才选择其成为leader继续工作。

unclean.leader.election.enable=true

策略2:选择任何一个活过来的副本,成为leader继续工作,此follower可能不在ISR中。

策略1,可靠性有保证,但是可用性低,只有最后挂了leader活过来kafka才能恢复。

策略2,可用性高,可靠性没有保证,任何一个副本活过来就可以继续工作,但是有可能存在数据不一致的情况。

4、kafka可靠性的保证

At most once:消息可能会丢,但绝不会重复传输。

At least once:消息绝不会丢,但可能会重复传输。

Exactly once:每条消息肯定会被传输一次且仅传输一次。

kafka最多保证At least once,可以保证不丢,但是可能会重复,为了解决重复需要引入唯一标识和去重机制,kafka提供了GUID实现了唯一标识,但是并没有提供自带的去重机制,需要开发人员基于业务规则自己去重。

文章目录
  1. 1. 一、kafka的存储机制
    1. 1.1. 1、segment
      1. 1.1.1. 1..log
      2. 1.1.2. 2..index
      3. 1.1.3. 3.命名规则
    2. 1.2. 2、读取数据
  2. 2. 二、可靠性保证
    1. 2.1. 1、AR
      1. 2.1.1. 1.ISR
      2. 2.1.2. 2.OSR
      3. 2.1.3. 3.LEO
      4. 2.1.4. 4.HW
      5. 2.1.5. 5.HW截断机制
    2. 2.2. 2、生产者可靠性级别
    3. 2.3. 3、leader选举
    4. 2.4. 4、kafka可靠性的保证