面试精选Java技术升华Kafka与Zookeeper

Kafka笔记

2022-05-04  本文已影响0人  斯文遮阳

一、背景知识

Kafka定义

传统定义:Kafka 是一个分布式的基于发布/订阅模式的消息队列,主要应用于大数据实时处理领域。

最新定义:Kafka 是一个开源的分布式事件流平台,被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。

消息队列

传统的消息队列的主要应用场景包括: 缓存/消峰、 解耦和异步通信。目前企业中比较常见的消息队列产品主要有 ActiveMQ、RabbitMQ、RocketMQ、Kafka 等。

消息队列的两种模式:

二、Kafka架构

Kafka架构
  1. Producer:消息生产者,就是向 kafka broker 发消息的客户端
  2. Consumer:消息消费者,向 kafka broker 取消息的客户端
  3. Consumer Group:消费者组,由多个 consumer 组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响,所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者
  4. Broker:一台 kafka 服务器就是一个 broker,一个集群由多个 broker 组成。一个 broker可以容纳多个 topic
  5. Topic:可以理解为一个队列,生产者和消费者面向的都是一个 topic
  6. Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列
  7. Replica:副本,为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka 仍然能够继续工作,kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,其中有一个 leader 和若干个 follower
  8. Leader:每个分区多个副本的主,生产者发送数据的对象,以及消费者消费数据的对象都是 leader。由 zk 记录谁是 leader,2.8.0 版本以后也可以配置不使用 zk
  9. Follower:每个分区多个副本中的从,实时从 leader 中同步数据,保持和 leader 数据的同步。leader 发生故障时,某个 follower 会成为新的 follower。

三、生产者

3.1 消息发送流程

在消息发送的过程中,涉及到了两个线程:main 线程和 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。Main 线程将消息发送给 RecordAccumulator,sender 线程不断从 RecordAccumulator 中拉取消息发送到 broker。

消息发送流程

几个重要参数:

几种消息发送方式:

3.2 分区

分区的好处:

生产者发送消息的分区策略:

  1. 指明 partition 的情况下,直接将指明的值直接作为 partiton 值
  2. 没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值
  3. 既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition 值,也就是常说的 round-robin 轮询算法

3.3 生产经验

生产者如何提高吞吐量

  1. 调整批次大小:如将 batch.size 由16k调整为32k
  2. 调整Sender线程等待时间:如将 linger.ms 由0调整为5-100ms
  3. 压缩策略:如将 compression.type 设为 snappy
  4. 调整缓存大小:如将 buffer.memory 由32m调整为64m

数据可靠性

Ack应答级别:

生产环境中,acks=0 很少使用;acks=1,一般用于传输普通日志,允许丢失个别数据;acks=-1,一般用于传输和交易相关等对可靠性要求较高的场景。

数据完全可靠条件 = ACK级别为-1 + 分区副本大于等于2 + ISR里应答的最小副本数大于等于2

数据重复性

至少一次(At Least Once)= ACK级别为1 + 分区副本大于等于2 + ISR里应答的最小副本数大于等于2。不能保证数据不重复。

最多一次(At Most Once)= ACK级别为0。不能保证数据不丢失。

精确一次(Exactly Once)= 幂等性 + 至少一次。幂等性默认开启,但只能保证在单分区单会话内不重复,如果需要全局严格一致,则需要开启事务(开启事务的前提是开启幂等性)。

数据顺序

单分区内,可以配置为有序:多分区,分区与分区间无序。

单分区有序的条件:

四、Broker

4.1 Broker启动流程

Kafka 集群中有一个 broker 的 controller 会被选举为 controller leader,负责管理集群 broker 的上下线、所有 topic 的分区副本分配和 leader 选举等工作。Controller 的信息同步工作是依赖于 zookeeper 的(2.8.0 版本以后也可以不依赖)。

Broker启动流程

4.2 副本与故障处理

副本

副本的作用是提高数据可靠性,Kafka 默认副本1个,生产环境一般配置为2个,保证数据可靠性;太多副本会增加磁盘存储空间,增加网络上数据传输,降低效率。

Kafka 中副本分为:leader 和 follower。Kafka 生产者只会把数据发往 leader,
然后 follower 找 leader 进行同步数据。

几个重要概念:

Follower 故障

  1. Follower 发生故障后会被临时提出 ISR
  2. 这个期间 leader 和 follower 继续接受数据
  3. 待该 follower 恢复后,follower 会读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步
  4. 等该 follower 的 LEO 大于等于该分区的 HW,即 follower 追上 leader 之后,就可以重新加入 ISR 了

Leader 故障

  1. Leader 发生故障之后,会从 ISR 中选出一个新的 leader
  2. 为保证多个副本之间的数据一致性,其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader 同步数据

注意: 这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。如何保证?见上一节数据可靠性。

4.3 文件存储

Topic 是逻辑上的概念,而 partition 是物理上的概念,每个 partition 对应一个 log 文件,该文件中存储的就是 producer 生产的数据。Producer 生产的数据会不断追加到该 log 文件末端。为防止 log 文件过大导致数据定位效率低下,kafka 采取了分片和索引机制,将每个 partition 分为多个 segment。每个 segment 包括:.index 文件、.log 文件和 .timeindex 等文件,这些文件位于一个文件夹下,该文件夹命名规则:topic 名称 + 分区序号,例如:first-0。

文件存储机制

两个重要参数:

Log 文件和 Index 文件示例

文件示例

高效读写数据

Kafka 如何做到高效读写数据?

  1. Kafka 本身是分布式集群,可以采用分区技术,并行度高
  2. 读数据采用稀疏索引,可以快速定位要消费的数据
  3. 顺序写磁盘,生产者数据是一直追加到 log 文件末端的顺序写(顺序写 600M/s vs 随机写 100K/s)
  4. 零拷贝+页缓存技术
    零拷贝:Kafka 的数据加工处理由生产者和消费者处理,broker 应用层不关心存储的数据,所以就不用了走应用层,传输效率高。
    页缓存:操作系统提供,当上层由写操作时,操作系统只是将数据写入 PageCache;读操作时先从 PageCache 中查找,找不到再去磁盘中获取。

关于零拷贝和页缓存,具体可以参考:https://zhuanlan.zhihu.com/p/258513662

五、消费者

5.1 消费方式

Consumer 采用 pull(拉)模式从 broker 中读取数据;因为 push (推)模式很难适应消费速率不同的消费者。

Pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有数据可供消费,consumer 会等待一段时间之后再返回,这段时长即为 timeout。

5.2 消费者组

消费者组(Consumer Group,CG)由多个 consumer 组成。形成一个消费者组的条件,是所有消费者的 groupid 相同。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响,所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。

消费者组初始化流程:


消费者组初始化流程

消费者组消费流程:


消费者组消费流程

5.3 分区的分配与再平衡

一个消费者组中有多个 consumer,一个 topic 有多个 partition,所以必然会涉及到 partition 的分配问题,即确定那个 partition 由哪个 consumer 来消费。当消费者组里面的消费者个数发生改变的时候,也会触发再平衡。

Kafka 有四种分配策略,可以通过参数 partition.assignment.strategy 来配置,默认 Range + CooperativeSticky。

5.4 Offset

由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢复后继续消费。

Kafka 0.9版本之前,consumer 默认将 offset 保存在 zookeeper 中;从 0.9 版本开始,默认将 offset 保存在 kafka 一个内置的 topic 中,该 topic 为__consumer_offsets。__consumer_offsets 主题里面采用 key 和 value 的方式存储数据。Key 是 group.id+topic+分区号,value 就是当前 offset 的值。 每隔一段时间,kafka 内部会对这个 topic 进行 compact,也就是每个 group.id+topic+分区号 就保留最新数据。

提交 offset

重复消费: 已经消费了数据,但是 offset 没提交。
漏消费: 先提交 offset 后消费,有可能会造成数据的漏消费。

如何避免漏消费和重复消费,做到精准一次消费呢?这依赖于消费者事务,要求消费端将消费过程和提交 offset 过程做原子绑定,也就是说需要将 offset 保存到支持事务的自定义介质(如 Mysql)。

指定 offset 消费

当 kafka 中没有初始偏移量(消费者组第一次消费)或服务器上不再存在当前偏移量时(例如该数据已被删除),该怎么办?有以下几种配置:

5.5 生产经验

如何提高吞吐量(避免数据积压)

六、Kafka-Kraft 模式

kafka架构

左图为 kafka 原有架构,元数据在 zookeeper 中,运行时动态选举 controller,由
controller 进行 kafka 集群管理。右图为 kraft 模式架构(实验性),不再依赖 zookeeper 集群,而是用三台 controller 节点代替 zookeeper,元数据保存在 controller 中,由 controller 直接进行 kafka 集群管理。这样做的好处有以下几个:

上一篇 下一篇

猜你喜欢

热点阅读