✨Kafka✨基础知识全面总结✨

521 阅读12分钟

你的名字-001

大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈


前言

Kafka的基础知识点还是蛮多的,本文针对Kafka的一些面试过程中常见的基础知识进行全面总结,方便大家进行查漏补缺。

正文

一. Broker概念

Kafka中,一个Kafka服务端的实例,就叫做一个Broker。已知Kafka使用Zookeeper来维护Kafka集群信息,如下图所示。

Kafka-Zookeeper存储信息脑图

Broker启动时,会在Zookeeper/brokers/ids路径上创建临时节点,将自己的id注册到Zookeeper

Kafka组件会订阅Zookeeper/brokers/ids路径,当有Broker加入或者退出集群时,这些Kafka组件就能够获得通知。

二. Topic概念

可以在Broker上创建Topic,生产者向Topic发送消息,消费者订阅Topic并从Topic拉取消息。可以用下图进行示意。

Kafka-Topic示意图

三. Partition概念

一个Topic可以有多个分区,这里的分区就叫做Partition,分区的作用是提高Kafka的吞吐量。

分区可以用下图进行示意。

Kafka-Partition示意图

可以用下面的指令在创建Topic的时候指定分区,指令如下所示。

./kafka-topics.sh --bootstrap-server 127.0.0.1:9092,127.0.0.1:9093,127.0.0.1:9094 --replication-factor 3 --partitions 3 --create --topic mytopic-0

上述指令会创建一个分区数为3,副本数为3Topic

一个分区可以有多个副本,且一个分区的多个副本不能在同一个Broker上,例如只有3Broker,但是创建Topic的时候,指定副本数为4,此时创建Topic会失败。

分区的多个副本可以分为leader节点和follower节点,且leader节点为客户端提供读写功能,follower节点会从leader节点同步数据但不提供读写功能,这样能避免出现读写不一致的问题。

创建Topic时,会在Broker上为这个Topic的分区创建目录,如下所示。

Topic分区目录创建图

分区目录的内容如下所示。

Topic分区目录内容图

每个文件含义如下。

  1. index文件是索引文件。记录消息的编号;
  2. timeindex文件是时间戳索引。记录生产者发送消息的时间和消息记录到日志文件的时间;
  3. log是日志文件。记录消息。

因为log文件会越来越大,此时根据index文件进行检索会影响效率,所以还需要对上述文件进行切分,切分出来的单位叫做Segment),可以通过log.segment.bytes来控制一段文件的大小。Segment的示意如下。

00000000000000000000.index
00000000000000000000.log
00000000000000000000.timeindex

00000000000000050000.index
00000000000000050000.log
00000000000000050000.timeindex

四. 消费者组

消费者组是Kafka提供的可扩展且具有容错性的消费者机制。一个消费者组内存在多个消费者,这些消费者共享一个Group ID。消费者组示意如下。

Kafka-消费者组示意图

关于消费者组,有如下说明。

  1. 消费者组的不同消费者不能同时消费同一个Partition
  2. 如果消费者组里消费者数量和Partition数量一样则一个消费者消费一个Partition
  3. 如果消费者组里消费者数量大于Partition数量则部分消费者会无法消费到Partition
  4. 如果消费者组里消费者数量小于Partition数量则部分消费者会消费多个Partition

五. 偏移量

偏移量,即Consumer Offset,用于存储消费者对Partition消费的位移量。

偏移量存储在Kafka的内部Topic中,这个内部Topic叫做_consumer_offsets,该Topic默认有50个分区,将消费者的Group ID进行hash后再对50取模,得到的结果对应的分区就会用于存储这个消费者的偏移量。

_ consumer_offsets的每条消息格式示意图如下所示。

Kafka-消费者偏移量消息结构图

注意_consumer_offsets是存储在Broker上的。

六. 生产者发送消息完整流程

生产者发送消息完整流程图如下所示。

Kafka-生产者发送消息流程图

结合上述流程图,对消息发送流程说明如下。

  1. 生产者生成消息Record
  2. Record经过拦截器链;
  3. keyvalue进行序列化;
  4. 使用自定义或者默认的分区器获取Record所属分区Partition
  5. Record放入消息累加器RecordAccumulator。根据TopicPartition,可以确定一个双端队列Deque,该队列每个节点为多条Record的合集即ProducerBatch,新Record会被添加到队列最后一个节点上;
  6. Sender将相同Broker节点的可发送ProducerBatch合并到一个Request中并发送。Sender会持续扫描RecordAccumulator中的ProducerBatch,只要满足大小为batch.size默认16K)或者最早Record等待已经超过linger.ms,该ProducerBatch就会被Sender收集,然后Sender会合并收集的相同BrokerProducerBatch到一个Request中并发送;
  7. 缓存请求RequestinFlightRequest缓冲区中。inFlightRequest中为每个Broker分配了一个队列,新Request会添加到队列头,每个队列最多容纳的Request个数由max.in.flight.requests.per.connection默认为5)控制,队列满后不会生成新Request
  8. Selector发送请求到Broker
  9. Broker收到并处理Request后,对Request进行ACK
  10. 客户端收到RequestACK后,将RequestinFlightRequest中移除。

七. 分区策略

Kafka中消息的分区计算策略小结如下。

  1. 消息中指定了分区。此时使用指定的分区;
  2. 消息中未指定分区但有自定义分区器。此时使用自定义分区器计算分区;
  3. 消息中未指定分区也没有自定义分区器但消息键不为空。此时对键求哈希值,并用求得的哈希值对Topic的分区数取模得到分区;
  4. 如果前面都不满足。此时根据Topic取一个递增整数并对Topic分区数求模得到分区。

八. ISR机制

当生产者向服务端发送消息后,通常需要等待服务端的ACK,这一过程可以用下图进行示意。

Kafka-服务端响应Producer示意图

Producer会将消息发送给Topic对应分区的leader节点,然后leaderfollower进行同步,如果全部正常的follower同步成功(follower完成消息落盘),那么服务端就可以向Producer发送ACK

上面描述中的正常follower的集合,叫做ISRIn-Sync Replica Set),只有与leader节点正常通信的follower才会被放入ISR中,换言之,只有ISR中的follower才有资格让leader等待同步结果。

如果leader挂掉,那么会在ISR中选择新的leader

九. ACK机制

Producer发送消息的时候,可以通过acks配置项来决定服务端返回ACK的策略,如下所示。

  1. acks设置为0Producer不需要等待服务端返回ACK,即Producer不关心服务端是否成功将消息落盘;
  2. acks设置为1leader成功将消息落盘便返回ACK,这是默认策略;
  3. acks设置为 -1leaderISR中全部follower落盘成功才返回ACK

十. Segment生成策略

分区在磁盘上由多个Segment组成,如下所示。

Topic分区目录内容图

有如下参数控制Segment的生成策略。

  1. log.segment.bytes。用于设置单个Segment大小,当某个Segment的大小超过这个值后,就需要生成新的Segment
  2. log.roll.hours。用于设置每隔多少小时就生成新的Segment
  3. log.index.size.max.bytes。当Segmentindex文件达到这个大小时,也需要生成新的Segment

十一. index文件

使用Kafka提供的kafka-dump-log.sh工具,可以打开index文件,打开后的index文件可以表示如下。

offset: 613     position: 5252
offset: 1284    position: 10986
offset: 1803    position: 17491
offset: 2398    position: 25792
offset: 3422    position: 35309
offset: 4446    position: 51690
offset: 5470    position: 68071
offset: 6494    position: 84452
offset: 7518    position: 100833

上述示例中,offset是偏移量,position表示这个offset对应的消息在log文件里的位置。

index文件建立的索引是稀疏索引,示意图如下。

Kafka-稀疏索引示例图

十二. timeindex文件

每一条被发送的消息都会记录时间戳,这里的时间戳可以是发送消息时间戳,或者是消息落盘时间戳。可以配置如下。

  1. log.message.timestamp.type设置为createtime。表示发送消息时间戳;
  2. log.message.timestamp.type设置为logappendtime。表示消息落盘时间戳。

十三. 索引检索过程

  1. 根据offset找到在哪个Segment中;
  2. Segmentindex文件根据offset找到消息的position
  3. 根据positionSegmentlog文件中最终找到消息。

十四. Partition存储总结

Partition存储示意图如下。

Kafka-Partition存储示意图

十五. Kafka中的Controller选举

ControllerKafka集群中负责对整个集群进行协调管理,比如完成分区分配Leader选举副本管理等。

关于Controller的选举,有如下注意点。

  1. 启动时选举。集群中Broker启动时会去Zookeeper创建临时节点 /controller,最先创建成功的Broker会成为Controller
  2. Controller异常时选举。如果Controller挂掉,此时其它Broker会通过Watch对象收到Controller变更的消息,然后就会尝试去Zookeeper创建临时节点 /controller,只会有一个Broker创建成功,创建失败的Broker会再次创建Watch对象来监视新Controller
  3. Borker异常。如果集群中某个非ControllerBroker挂掉,此时Controller会检查挂掉的Broker上是否有某个分区的leader副本,如果有,则需要为这个分区选举新的leader副本,并更新分区的ISR集合;
  4. Broker加入。如果有一个Broker加入集群,则Controller会去判断新加入的Broker中是否含有当前已有分区的副本,如果有,那么需要去从leader副本中同步数据。

十六. 分区leader副本选举

一个分区有三种集合,如下所示。

  1. ARAssigned Replicas)。分区中的所有副本;
  2. ISRIn-Sync Replicas)。与leader副本保持一定同步程度的副本,ISR包括leader副本自身;
  3. OSROut-of-Sync Replicas)。与leader副本同步程度滞后过多的副本。

上述三种集合的关系是AR = ISR + OSR

分区leader副本选举有如下注意点。

  1. leader副本会维护和跟踪ISR中所有副本与leader副本的同步程度,如果某个副本的同步程度滞后过多,则leader副本会将这个副本从ISR中移到OSR中;
  2. OSR中有副本重新与leader副本保持一定同步程度,则leader副本会将其从OSR中移到ISR
  3. leader副本发生故障时,Controller会负责为这个分区从ISR中选举新的leader副本。

十七. 主从同步

分区的leaderfollower之间的主从同步示意图如下。

Kafka-主从同步示意图

有两个重要概念如下所示。

  1. LEOLog End Offset)。下一条待写入消息的Offset
  2. HWHigh Watermark)。ISR集合中的最小LEO

那么对于上图而言,HW6,那么消费者最多只能消费到HW之前的消息,也就是Offset5的消息。

主从同步规则如下。

  1. follower会向leader发送fetch请求,然后leaderfollower发送数据;
  2. follower接收到数据后,依次写入消息并且更新LEO
  3. leader最后会更新HW

leaderfollower发生故障时,处理策略如下。

  1. 如果follower挂掉,那么当follower恢复后,需要先将HWHW之后的数据丢弃,然后再向leader发起同步;
  2. 如果leader挂掉,则会先从follower中选择一个成为leader,然后其它followerHWHW之后的数据丢弃,然后再向leader发起同步。

十八. Kafka为什么快

  1. 顺序读写
  2. 索引
  3. 批量读写和文件压缩
  4. 零拷贝

十九. 零拷贝

如果要将磁盘中的文件内容,发送到远程服务器,则整个数据流转如下所示。

Kafka-传统IO示意图

步骤说明如下。

  1. 从磁盘文件读取文件内容,并拷贝到内核缓冲区;
  2. CPU控制器将内核缓冲区的数据拷贝到用户缓冲区;
  3. 应用程序中调用write() 方法,将用户缓冲区的数据拷贝到Socket缓冲区;
  4. Socket缓冲区的数据拷贝到网卡。

一共经历了四次拷贝(和四次CPU上下文切换),其中如下两次拷贝是多余的。

  1. 内核缓冲区拷贝到用户缓冲区;
  2. 用户缓冲区拷贝到Socket缓冲区。

Kafka中的零拷贝,就是将上述两次多余的拷贝省掉,示意图如下。

Kafka-零拷贝示意图

零拷贝步骤如下所示。

  1. 从磁盘文件读取文件内容,并拷贝到内核缓冲区;
  2. 将文件描述符和数据长度加载到Socket缓冲区;
  3. 将数据直接从内核缓冲区拷贝到网卡。

一共只会经历两次拷贝(和两次CPU上下文切换)。


大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈

你的名字-002