三天吃透 Kafka 面试八股文
本文已经收录到 Github 仓库,该仓库包含计算机基础、Java 基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎 star~
Github 地址:https://github.com/Tyson0314/Java-learning
Kafka 都有哪些特点?
高吞吐量、低延迟:kafka 每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个 topic 可以分多个 partition, consumer group 对 partition 进行 consume 操作。
可扩展性:kafka 集群支持热扩展
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
容错性:允许集群中节点失败(若副本数量为 n,则允许 n-1 个节点失败)
高并发:支持数千个客户端同时读写
请简述下你在哪些场景下会选择 Kafka?
日志收集:一个公司可以用 Kafka 可以收集各种服务的 log,通过 kafka 以统一接口服务的方式开放给各种 consumer,例如 hadoop、HBase、Solr 等。
消息系统:解耦和生产者和消费者、缓存消息等。
用户活动跟踪:Kafka 经常被用来记录 web 用户或者 app 用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到 kafka 的 topic 中,然后订阅者通过订阅这些 topic 来做实时的监控分析,或者装载到 hadoop、数据仓库中做离线分析和挖掘。
运营指标:Kafka 也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
流式处理:比如 spark streaming 和 Flink
Kafka 的设计架构你知道吗?
Kafka 架构分为以下几个部分:
Producer :消息生产者,就是向 kafka broker 发消息的客户端。
Consumer :消息消费者,向 kafka broker 取消息的客户端。
Topic :可以理解为一个队列,一个 Topic 又分为一个或多个分区,
Consumer Group:这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。
Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。
Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker 上,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的 id(offset)。将消息发给 consumer,kafka 只保证按一个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。
Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。
Kafka 分区的目的?
分区对于 Kafka 集群的好处是:实现负载均衡。分区对于消费者来说,可以提高并发度,提高效率。
你知道 Kafka 是如何做到消息的有序性?
kafka 中的每个 partition 中的消息在写入时都是有序的,而且单独一个 partition 只能由一个消费者去消费,可以在里面保证消息的顺序性。但是分区之间的消息是不保证有序的。
Kafka Producer 的执行过程?
1,Producer 生产消息 --> 2,从 Zookeeper 找到 Partition 的 Leader --> 3,推送消息 --> 4,通过 ISR 列表通知给 Follower --> 5, Follower 从 Leader 拉取消息,并发送 ack --> 6,Leader 收到所有副本的 ack,更新 Offset,并向 Producer 发送 ack,表示消息写入成功。
讲一下你使用 Kafka Consumer 消费消息时的线程模型,为何如此设计?
Thread-Per-Consumer Model,这种多线程模型是利用 Kafka 的 topic 分多个 partition 的机制来实现并行:每个线程都有自己的 consumer 实例,负责消费若干个 partition。各个线程之间是完全独立的,不涉及任何线程同步和通信,所以实现起来非常简单。
请谈一谈 Kafka 数据一致性原理
一致性就是说不论是老的 Leader 还是新选举的 Leader,Consumer 都能读到一样的数据。
假设分区的副本为 3,其中副本 0 是 Leader,副本 1 和副本 2 是 follower,并且在 ISR 列表里面。虽然副本 0 已经写入了 Message4,但是 Consumer 只能读取到 Message2。因为所有的 ISR 都同步了 Message2,只有 High Water Mark 以上的消息才支持 Consumer 读取,而 High Water Mark 取决于 ISR 列表里面偏移量最小的分区,对应于上图的副本 2,这个很类似于木桶原理。
这样做的原因是还没有被足够多副本复制的消息被认为是“不安全”的,如果 Leader 发生崩溃,另一个副本成为新 Leader,那么这些消息很可能丢失了。如果我们允许消费者读取这些消息,可能就会破坏一致性。试想,一个消费者从当前 Leader(副本 0) 读取并处理了 Message4,这个时候 Leader 挂掉了,选举了副本 1 为新的 Leader,这时候另一个消费者再去从新的 Leader 读取消息,发现这个消息其实并不存在,这就导致了数据不一致性问题。
当然,引入了 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 值。
数据传输的事务有几种?
数据传输的事务定义通常有以下三种级别:
(1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输(2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.(3)精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被
Kafka 消费者是否可以消费指定分区消息?
Kafa consumer 消费消息时,向 broker 发出 fetch 请求去消费特定分区的消息,consumer 指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer 拥有了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的。
Kafka 消息是采用 Pull 模式,还是 Push 模式?
Kafka 最初考虑的问题是,customer 应该从 brokes 拉取消息还是 brokers 将消息推送到 consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 broker,consumer 从 broker 拉取消息。
一些消息系统比如 Scribe 和 Apache Flume 采用了 push 模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率时,consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式。
Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的消费能力去决定这些策略。
Pull 有个缺点是,如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到 t 达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
Kafka 高效文件存储设计特点
Kafka 把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
通过索引信息可以快速定位 message 和确定 response 的最大大小。
通过 index 元数据全部映射到 memory,可以避免 segment file 的 IO 磁盘操作。
通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小
Kafka 创建 Topic 时如何将分区放置到不同的 Broker 中
副本因子不能大于 Broker 的个数;
第一个分区(编号为 0)的第一个副本放置位置是随机从 brokerList 选择的;
其他分区的第一个副本放置位置相对于第 0 个分区依次往后移。也就是如果我们有 5 个 Broker,5 个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的
谈一谈 Kafka 的再均衡
在 Kafka 中,当有新消费者加入或者订阅的 topic 数发生变化时,会触发 Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance 顾名思义就是重新均衡消费者消费。Rebalance 的过程如下:
第一步:所有成员都向 coordinator 发送请求,请求入组。一旦所有成员都发送了请求,coordinator 会从中选择一个 consumer 担任 leader 的角色,并把组成员信息以及订阅信息发给 leader。
第二步:leader 开始分配消费方案,指明具体哪个 consumer 负责消费哪些 topic 的哪些 partition。一旦完成分配,leader 会将这个方案发给 coordinator。coordinator 接收到分配方案之后会把方案发给各个 consumer,这样组内的所有成员就都知道自己应该消费哪些分区了。
所以对于 Rebalance 来说,Coordinator 起着至关重要的作用
Kafka 是如何实现高吞吐率的?
Kafka 是分布式消息系统,需要处理海量的消息,Kafka 的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。kafka 主要使用了以下几个方式实现了超高的吞吐率:
顺序读写;
零拷贝
文件分段
批量发送
数据压缩。
Kafka 缺点?
由于是批量发送,数据并非真正的实时;
对于 mqtt 协议不支持;
不支持物联网传感数据直接接入;
仅支持统一分区内消息有序,无法实现全局消息有序;
监控不完善,需要安装插件;
依赖 zookeeper 进行元数据管理;
Kafka 新旧消费者的区别
旧的 Kafka 消费者 API 主要包括:SimpleConsumer(简单消费者) 和 ZookeeperConsumerConnectir(高级消费者)。SimpleConsumer 名字看起来是简单消费者,但是其实用起来很不简单,可以使用它从特定的分区和偏移量开始读取消息。高级消费者和现在新的消费者有点像,有消费者群组,有分区再均衡,不过它使用 ZK 来管理消费者群组,并不具备偏移量和再均衡的可操控性。
现在的消费者同时支持以上两种行为,所以为啥还用旧消费者 API 呢?
Kafka 分区数可以增加或减少吗?为什么?
我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。
最后给大家分享一个 Github 仓库,上面有大彬整理的 300 多本经典的计算机书籍 PDF,包括 C 语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以 star 一下,下次找书直接在上面搜索,仓库持续更新中~
Github 地址:https://github.com/Tyson0314/java-books
评论