Kafka 面试 22 连问,看完直接跟面试官聊骚都没问题
这不是秋招马上开始了嘛,这个月我每天会分享一个技术栈的高频面试题,而这些面试题都是取自于我五月份时整理的一些面试文档,希望对最近有面试或者有跳槽打算的同学有所帮助了,打开方式 👉Java25技术栈
废话不多说,开始今天的面试之旅
1、为什么要使用 kafka,为什么要使用消息队列?
缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka 在中间可以起到一个缓冲的作用,把消息暂存在 kafka 中,下游服务就可以按照自己的节奏进行慢慢处理。
解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。
冗余:可以采用一对多的方式,一个生产者发布消息,可以被多个订阅 topic 的服务消费到,供多个毫无关联的业务使用。
健壮性:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。
异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
2、Kafka 的常用组件有哪些?
3、数据传输的事物定义有哪三种?数据传输的事务定义通常有以下三种级别:
最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输。
最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输。
精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被一次而 且仅仅被传输一次,这是大家所期望的。
4、ZooKeeper 在 Kafka 中的作用是什么?
Apache Kafka 是一个使用 Zookeeper 构建的分布式系统。虽然,Zookeeper 的主要作用是在集群中的不同节点之间建立协调。但是,如果任何节点失败,我们还使用 Zookeeper 从先前提交的偏移量中恢复,因为它做周期性提交偏移量工作。
5、没有 ZooKeeper 可以使用 Kafka 吗?
zookeeper 是一个分布式的协调组件,早期版本的 kafka 用 zk 做 meta 信息存储,consumer 的消费状态,group 的管理以及 offset 的值。考虑到 zk 本身的一些因素以及整个架构较大概率存在单点问题,新版本中逐渐弱化了 zookeeper 的作用。新的 consumer 使用了 kafka 内部的 group coordination 协议,也减少了对 zookeeper 的依赖。但是 broker 依然依赖于 ZK,zookeeper 在 kafka 中还用来选举 controller 和 检测 broker 是否存活等等。
6、Kafka 判断一个节点是否还活着有那两个条件?
节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接。
如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久。
7、解释偏移的作用。
给分区中的消息提供了一个顺序 ID 号,我们称之为偏移量。因此,为了唯一地识别分区中的每条消息,我们使用这些偏移量。
8、producer 是否直接将数据发送到 broker 的 leader(主节点)?
producer 直接将数据发送到 broker 的 leader(主节点),不需要在多个节点进行分发,为了 帮助 producer 做到这点,所有的 Kafka 节点都可以及时的告知:哪些节点是活动的,目标 topic 目标分区的 leader 在哪。这样 producer 就可以直接将消息发送到目的地了。
9、Kafa consumer 是否可以消费指定分区消息?
Kafa consumer 消费消息时,向 broker 发出"fetch"请求去消费特定分区的消息,consumer 指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer 拥有 了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的。
10、Kafka 存储在硬盘上的消息格式是什么?
消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和 CRC32 校验码。
消息长度: 4 bytes (value: 1+4+n)
版本号: 1 byte
CRC 校验码: 4 bytes
具体的消息: n bytes
11、kafka follower 如何与 leader 同步数据?
Kafka 的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求 All Alive Follower 都复制完,这条消息才会被认为 commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower 异步的从 Leader 复制数据,数据只要被 Leader 写入 log 就被认为已经 commit,这种情况下,如果 leader 挂掉,会丢失数据,kafka 使用 ISR 的方式很好的均衡了确保数据不丢失以及吞吐率。Follower 可以批量的从 Leader 复制数据,而且 Leader 充分利用磁盘顺序读以及 send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了 Follower 与 Leader 的消息量差。
12、Kafka 高效文件存储设计特点:
Kafka 把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定 期清除或删除已经消费完文件,减少磁盘占用。
通过索引信息可以快速定位 message 和确定 response 的最大大小。
通过 index 元数据全部映射到 memory,可以避免 segment file 的 IO 磁盘操作。
通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。
13、Kafka 与传统消息系统之间有三个关键区别
Kafka 持久化日志,这些日志可以被重复读取和无限期保留
Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据 提升容错能力和高可用性
Kafka 支持实时的流式处理
14、Kafka 为什么那么快?
Cache Filesystem Cache PageCache 缓存
顺序写 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。
Zero-copy 零拷技术减少拷贝次数
Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。
Pull 拉模式 使用拉模式进行消息的获取消费,与消费端处理能力相符。
15、什么情况下一个 broker 会从 isr 中踢出去?
leader 会维护一个与其基本保持同步的 Replica 列表,该列表称为 ISR(in-sync Replica),每个 Partition 都会有一个 ISR,而且是由 leader 动态维护 ,如果一个 follower 比一个 leader 落后太多,或者超过一定时间未发起数据复制请求,则 leader 将其重 ISR 中移除 。
16、kafka producer 如何优化打入速度?
增加线程
提高 batch.size
增加更多 producer 实例
增加 partition 数
设置 acks=-1 时,如果延迟增大:可以增大 num.replica.fetchers(follower 同步数据的线程数)来调解;
跨数据中心的传输:增加 socket 缓冲区设置以及 OS tcp 缓冲区设置。
17、kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥(ack 机制), 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit 了?
1(默认) 数据发送到 Kafka 后,经过 leader 成功接收消息的的确认,就算是发送成功了。在这种情况下,如果 leader 宕机了,则会丢失数据。
0 生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
-1 producer 需要等待 ISR 中的所有 follower 都确认接收到数据后才算一次发送完成,可靠性最高。当 ISR 中所有 Replica 都向 Leader 发送 ACK 时,leader 才 commit,这时候 producer 才能认为一个请求中的消息都 commit 了。
18、Kafka 中的消息是否会丢失和重复消费?
要确定 Kafka 的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费。
1、消息发送
Kafka 消息发送有两种方式:同步(sync)和异步(async),默认是同步方式,可通过 producer.type 属性进行配置。Kafka 通过配置 request.required.acks 属性来确认消息的生产:
0---表示不进行消息接收是否成功的确认;
1---表示当 Leader 接收成功时确认;
-1---表示 Leader 和 Follower 都接收成功时确认;
综上所述,有 6 种消息生产的情况,下面分情况来分析消息丢失的场景:
(1)acks=0,不和 Kafka 集群进行消息接收确认,则当网络异常、缓冲区满了等情况时,消息可能丢失;
(2)acks=1、同步模式下,只有 Leader 确认接收成功后但挂掉了,副本没有同步,数据可能丢失;
2、消息消费
Kafka 消息消费有两个 consumer 接口,Low-level API 和 High-level API:
Low-level API:消费者自己维护 offset 等值,可以实现对 Kafka 的完全控制;
High-level API:封装了对 parition 和 offset 的管理,使用简单;
如果使用高级接口 High-level API,可能存在一个问题就是当消息消费者从集群中把消息取出来、并提交了新的消息 offset 值后,还没来得及消费就挂掉了,那么下次再消费时之前没消费成功的消息就“诡异”的消失了;
解决办法:
针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入 Leader 和 Follower 之后再确认消息发送成功;异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态;
针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。
消息重复消费及解决参考:https://www.javazhiyin.com/22910.html
19、为什么 Kafka 不支持读写分离?
在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。
Kafka 并不支持主写从读,因为主写从读有 2 个很明 显的缺点:
数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间 窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中 A 数据的值都为 X, 之后将主节点中 A 的值修改为 Y,那么在这个变更通知到从节点之前,应用读取从节点中的 A 数据的值并不为最新的 Y,由此便产生了数据不一致的问题。
延时问题。类似 Redis 这种组件,数据从写入主节点到同步至从节点中的过程需要经 历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在 Kafka 中,主从同步会比 Redis 更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节 点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。
20、Kafka 中是怎么体现消息顺序性的?
kafka 每个 partition 中的消息在写入时都是有序的,消费时,每个 partition 只能被每一个 group 中的一个消费者消费,保证了消费时也是有序的。
整个 topic 不保证有序。如果为了保证 topic 整个有序,那么将 partition 调整为 1.
21、消费者提交消费位移时提交的是当前消费到的最新消息的 offset 还是 offset+1?
offset+1
22、kafka 如何实现延迟队列?
Kafka 并没有使用 JDK 自带的 Timer 或者 DelayQueue 来实现延迟的功能,而是基于时间轮自定义了一个用于实现延迟功能的定时器(SystemTimer)。JDK 的 Timer 和 DelayQueue 插入和删除操作的平均时间复杂度为 O(nlog(n)),并不能满足 Kafka 的高性能要求,而基于时间轮可以将插入和删除操作的时间复杂度都降为 O(1)。时间轮的应用并非 Kafka 独有,其应用场景还有很多,在 Netty、Akka、Quartz、Zookeeper 等组件中都存在时间轮的踪影。
底层使用数组实现,数组中的每个元素可以存放一个 TimerTaskList 对象。TimerTaskList 是一个环形双向链表,在其中的链表项 TimerTaskEntry 中封装了真正的定时任务 TimerTask.
Kafka 中到底是怎么推进时间的呢?Kafka 中的定时器借助了 JDK 中的 DelayQueue 来协助推进时间轮。具体做法是对于每个使用到的 TimerTaskList 都会加入到 DelayQueue 中。
Kafka 中的 TimingWheel 专门用来执行插入和删除 TimerTaskEntry 的操作,而 DelayQueue 专门负责时间推进的任务。再试想一下,DelayQueue 中的第一个超时任务列表的 expiration 为 200ms,第二个超时任务为 840ms,这里获取 DelayQueue 的队头只需要 O(1)的时间复杂度。
如果采用每秒定时推进,那么获取到第一个超时的任务列表时执行的 200 次推进中有 199 次属于“空推进”,而获取到第二个超时任务时有需要执行 639 次“空推进”,这样会无故空耗机器的性能资源,这里采用 DelayQueue 来辅助以少量空间换时间,从而做到了“精准推进”。Kafka 中的定时器真可谓是“知人善用”,用 TimingWheel 做最擅长的任务添加和删除操作,而用 DelayQueue 做最擅长的时间推进工作,相辅相成。
评论