Kafka 面试考点

Kafka 特点

高吞吐量, 低延迟; 每秒可处理几十万条消息, 延迟最低只有几毫秒

可扩展性:集群支持热扩展

持久性,可靠性:消息持久化到本地磁盘,支持数据备份防止数据丢失。

容错性:允许集群中节点失败,(集群节点数量为n, 允许n-1个节点失败)

高并发: 支持数千个客户端读写。

Kafka 分区目的

对于kafka集群, 实现负载均衡, 对于消费者,提高并行度,提高效率

Kafka 消息有序性

每个partition消息写入时都是有序的,单独一个partition只能由一个消费者进行消费,保证partition内消息有序,分区间消息无序。

Kafka 高可靠性

保证高可靠性几个角度: Producer往 Broker发送消息, Topic 分区副本, leader选举 。

Topic  分区副本

Kafka 从 0.8.0 版本开始引入了分区副本, 每个分区可以人为的 配置几个副本(比如创建主题的时候指定 replication-factor,也可以在 Broker 级别进行配置 default.replication.factor),一般会设置为3。

Kafka 可以保证单个分区里的事件是有序的,众多的分区副本里面有一个副本是 Leader,其余的副本是 follower,所有的读写操作都是经过 Leader 进行的,同时 follower 会定期地去 leader 上的复制数据。当 Leader 挂了的时候,其中一个 follower 会重新成为新的 Leader。通过分区副本,引入了数据冗余,同时也提供了 Kafka 的数据可靠性。

Producer往Broker 发送消息

为了让用户设置数据可靠性, Kafka 在 Producer 里面提供了消息确认机制。也就是说我们可以通过配置来决定消息发送到对应分区的几个副本才算消息发送成功。可以在定义 Producer 时通过 acks 参数指定

acks = 0:意味着如果生产者能够通过网络把消息发送出去,那么就认为消息已成功写入 Kafka 。在这种情况下还是有可能发生错误,比如发送的对象无能被序列化或者网卡发生故障,在 acks=0 模式下的运行速度是非常快的,不过如果选择了这种模式, 一定会丢失一些消息。

acks = 1:意味若 Leader 在收到消息并把它写入到分区数据文件(不一定同步到磁盘上)时会返回确认或错误响应。不过在这个模式下仍然有可能丢失数据,比如消息已经成功写入 Leader,但在消息被复制到 follower 副本之前 Leader发生崩溃。

acks = all:意味着 Leader 在返回确认或错误响应之前,会等待所有同步副本都收到悄息。如果和 min.insync.replicas 参数结合起来,就可以决定在返回确认前至少有多少个副本能够收到悄息,生产者会一直重试直到消息被成功提交

另外,Producer 发送消息还可以选择同步(默认,通过 producer.type=sync 配置) 或者异步(producer.type=async)模式。如果设置成异步,虽然会极大的提高消息发送的性能,但是这样会增加丢失数据的风险。如果需要确保消息的可靠性,必须将 producer.type 设置为 sync。

Leader 选举

每个分区的 leader 会维护一个 ISR 列表,ISR 列表里面就是 follower 副本的 Borker 编号,只有跟得上 Leader 的 follower 副本才能加入到 ISR 里面,这个是通过 replica.lag.time.max.ms 参数配置的,


       Kafka 复制机制

Kafka 主题中的每个分区都有一个预写日志(write-ahead log),我们写入 Kafka 的消息就存储在这里面。这里面的每条消息都有一个唯一的偏移量,用于标识它在当前分区日志中的位置

Kafka 中的每个主题分区都被复制了 n 次,其中的 n 是主题的复制因子(replication factor)。这允许 Kafka 在集群服务器发生故障时自动切换到这些副本,以便在出现故障时消息仍然可用

Kafka 的复制是以分区为粒度的,分区的预写日志被复制到 n 个服务器。 在 n 个副本中,一个副本作为 leader,其他副本成为 followers。顾名思义,producer 只能往 leader 分区上写数据(读也只能从 leader 分区上进行),followers 只按顺序从 leader 上复制日志。

每个分区的 leader 会维护一个 in-sync replica(同步副本列表,又称 ISR)。当 producer 往 broker 发送消息,消息先写入到对应 leader 分区上,然后复制到这个分区的所有副本中。只有将消息成功复制到所有同步副本(ISR)后,这条消息才算被提交

由于消息复制延迟受到最慢同步副本的限制,因此快速检测慢副本并将其从 ISR 中删除非常重要。

    副本在什么情况下才算跟上Leader

0.10 版本前, 两个参数:

replica.lag.max.messages 设置为4,这意味着只要 follower 落后 leader 的消息不超过3条,它就不会从 ISR 中删除。replica.lag.time.max.ms 设置为500毫秒,这意味着只要 follower 每隔500毫秒或更早地向 leader 发送一个 fetch 请求,它们就不会被标记为死亡并且不会从 ISR 中删除。

   什么情况下导致副本与leader 失去同步:

  • 慢副本(Slow replica):follower replica 在一段时间内一直无法赶上 leader 的写进度。造成这种情况的最常见原因之一是 follower replica 上的 I/O瓶颈,导致它持久化日志的时间比它从 leader 消费消息的时间要长;
  • 卡住副本(Stuck replica):follower replica 在很长一段时间内停止从 leader 获取消息。这可能是以为 GC 停顿,或者副本出现故障;
  • 刚启动副本(Bootstrapping replica):当用户给某个主题增加副本因子时,新的 follower replicas 是不同步的,直到它跟上 leader 的日志。

延迟消息 检测 慢副本, 延迟时间检测 卡住副本。

   延迟消息检测 的问题

如果延迟消息设置为4, 生产者发送速率为2msg/sec, 那么follower不会被移除ISR,如果峰值时传入4msg, 那么follwer 副本都会视为不同步,被移除ISR.

由于两个 follower 副本都处于活动状态,因此它们将在下一个 fetch 请求中 ( 设置的延迟时间 )     赶上 leader 的日志结束偏移量并被添加回 ISR。 这样会频繁进入ISR.

 延迟消息的   参数的核心问题是,用户必须猜测如何配置这个值,因为我们不知道 Kafka 的传入流量到底会到多少,特别是在网络峰值的情况下。

所以就 去掉了 延迟消息 这个参数, 只根据延迟时间一个参数来决定 副本是否  移除ISR. 


只有 ISR 里的成员才有被选为 leader 的可能。所以当 Leader 挂掉了,而且 unclean.leader.election.enable=false 的情况下,Kafka 会从 ISR 列表中选择第一个 follower 作为新的 Leader,因为这个分区拥有最新的已经 committed 的消息。通过这个可以保证已经 committed 的消息的数据可靠性。

Kafka 数据一致性

这里的数据一致性主要是说不论是老的 Leader 还是新选举的 Leader,Consumer 都能读到一样的数据

引入 High Water Mark ,  High Water Mark 取决于 ISR 列表里面偏移量最小的分区,只有 High Water Mark 以上的消息才支持 Consumer 读取。

引入了 High Water Mark 机制,会导致 Broker 间的消息复制因为某些原因变慢,那么消息到达消费者的时间也会随之变长(因为我们会先等待消息复制完毕)延迟时间可以通过参数 replica.lag.time.max.ms 参数配置,它指定了副本在复制消息时可被允许的最大延迟时间。

ISR, OSR, AR

ISR:In-Sync Replicas 副本同步队列

OSR:Out-of-Sync Replicas

AR:Assigned Replicas 所有副本

ISR是由leader维护,follower从leader同步数据有一些延迟(具体可以参见 图文了解 Kafka 的副本复制机制),超过相应的阈值会把 follower 剔除出 ISR, 存入OSR(Out-of-Sync Replicas )列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。

LEO、HW、LSO、LW等分别代表什么

  • LEO:是 LogEndOffset 的简称,代表当前日志文件中下一条
  • HW:水位或水印(watermark)一词,也可称为高水位(high watermark),通常被用在流式处理领域(比如Apache Flink、Apache Spark等),以表征元素或事件在基于时间层面上的进度。在Kafka中,水位的概念反而与时间无关,而是与位置信息相关。严格来说,它表示的就是位置信息,即位移(offset)。取 partition 对应的 ISR中 最小的 LEO 作为 HW,consumer 最多只能消费到 HW 所在的位置上一条信息。
  • LSO:是 LastStableOffset 的简称,对未完成的事务而言,LSO 的值等于事务中第一条消息的位置(firstUnstableOffset),对已完成的事务而言,它的值同 HW 相同
  • LW:Low Watermark 低水位, 代表 AR 集合中最小的 logStartOffset 值。

Kafka 分区只能由一个消费者线程消费, 如何做到多个线程同时消费一个分区

数据传输事务

  • 最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输
  • 最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.
  • 精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被

消费者是否可以消费指定分区消息

consumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥有了offset的控制权,可以向后回滚去重新消费之前的消息,

消费者消费消息采用Pull 模式, 还是push 模式

push模式:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了

Kafka还是选取了传统的pull模式

Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发

Kafka 创建Topic 如何把分区放置不同topic中

  • 副本因子不能大于 Broker 的个数;
  • 第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
  • 其他分区的第一个副本放置位置相对于第0个分区依次往后移。也就是如果我们有5个 Broker,5个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
  • 剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的

Kafka 新建分区会在哪个目录下创建

启动 Kafka 集群之前,我们需要配置好 log.dirs 参数,其值是 Kafka 数据的存放目录,

如果 log.dirs 参数只配置了一个目录,那么分配到各个 Broker 上的分区肯定只能在这个目录下创建文件夹用于存放数据。

如果 log.dirs 参数配置了多个目录,那么 Kafka 会在哪个文件夹中创建分区目录呢?答案是:Kafka 会在含有分区目录最少的文件夹中创建新的分区目录,分区目录名为 Topic名+分区ID。注意,是分区文件夹总数最少的目录,而不是磁盘使用量最少的目录!也就是说,如果你给 log.dirs 参数新增了一个新的磁盘,新的分区目录肯定是先在这个新的磁盘上创建直到这个新的磁盘目录拥有的分区目录不是最少为止。

需要创建新的分区时,Kafka先从 logs 存储池【 logs = new Pool[TopicAndPartition, Log]() 】中获取当前分区对应的 Log 对象。如果获取到了,说明不是新的分区,这时候直接返回 Log 实例;如果这个分区是新建的,肯定是获取不到,这时候需要调用 nextLogDir 函数获取再哪个目录上创建分区目录。其核心思想就是找到分区数最少的目录来创建新的分区。

问题:

  • 分区数最少的目录未必是数据量最少的目录,如果分区数最少的目录恰恰是数据量最多的目录这样会导致磁盘使用不均衡;
  • 这种实现也没有考虑到磁盘的读写负载。

Kafka  在均衡

在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义就是重新均衡消费者消费。Rebalance的过程如下:

第一步:所有成员都向coordinator【协调器】发送请求,请求入组。一旦所有成员都发送了请求,coordinator会从中选择一个consumer担任leader的角色,并把组成员信息以及订阅信息发给leader。
第二步:leader开始分配消费方案,指明具体哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案发给coordinator。coordinator接收到分配方案之后会把方案发给各个consumer,这样组内的所有成员就都知道自己应该消费哪些分区了。
所以对于Rebalance来说,Coordinator起着至关重要的作用

Kafka  分区分配 策略

每个 Topic 一般会有很多个 partitions。为了使得我们能够及时消费消息,我们也可能会启动多个 Consumer 去消费,而每个 Consumer 又会启动一个或多个streams【消费者线程】去分别消费 Topic 对应分区中的数据。

每个分区只能由同一个消费组内的一个consumer来消费

问题来了,同一个 Consumer Group 里面的 Consumer 是如何知道该消费哪些分区里面的数据呢?

Kafka 内部存在两种默认的分区分配策略:Range 和 RoundRobin

分区分配策略触发条件:

  • 同一个 Consumer Group 内新增消费者
  • 消费者离开当前所属的Consumer Group,包括shuts down 或 crashes
  • 订阅的主题新增分区

Range 策略:默认

Range策略是对每个主题而言的,首先对同一个主题里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序

然后将partitions的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区

问题:没有实现真正的负载均衡。

Roundrobin  将所有主题的分区组成 TopicAndPartition 列表,然后对 TopicAndPartition 列表按照 hashCode 进行排序,除以消费者所有消费线程总数

前提:

  • 同一个Consumer Group里面的所有消费者的num.streams必须相等;
  • 每个消费者订阅的主题必须相同。

Kafka 如何实现高吞吐率的

顺序读写

kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能

顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写

Kafka官方给出了测试数据(Raid-5,7200rpm):

顺序 I/O: 600MB/s

随机 I/O: 100KB/s

零拷贝:

一个程序要把文件内容发送到网络,这个程序是工作在用户空间,文件和网络socket属于硬件资源,两者之间有一个内核空间

零拷贝(zero-copy)"系统调用机制,就是跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”

文件分段

topic被分为了多个区partition,每个partition又分为多个段segment,所以一个队列中的消息实际上是保存在N多个片段文件中

通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力

批量发送

Kafka允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去

比如可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去

如100条消息就发送,或者每5秒发送一次

这种策略将大大减少服务端的I/O次数

数据压缩

Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩

压缩的好处就是减少传输的数据量,减轻对网络传输的压力

Producer压缩之后,在Consumer需进行解压,虽然增加了CPU的工作,但在对大数据处理上,瓶颈在网络上而不是CPU,所以这个成本很值得

Kafka 缺点

  • 由于是批量发送,数据并非真正的实时;
  • 对于mqtt协议不支持;
  • 不支持物联网传感数据直接接入;
  • 仅支持统一分区内消息有序,无法实现全局消息有序;
  • 监控不完善,需要安装插件;
  • 依赖zookeeper进行元数据管理;

kafka  分区数可以减少吗?

我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。

Kafka  幂等性

kafka 事务性

发布了12 篇原创文章 · 获赞 0 · 访问量 255

猜你喜欢

转载自blog.csdn.net/zhzsdiligence/article/details/102367838