技术栈 | 传送门 |
JAVA 基础 | 手撸架构,Java基础面试100问_vincent-CSDN博客 |
JAVA 集合 | 手撸架构,JAVA集合面试60问_vincent-CSDN博客 |
JVM 虚拟机 | 手撸架构,JVM面试30问_vincent-CSDN博客 |
并发编程 | 手撸架构,并发编程面试123问_vincent-CSDN博客 |
Spring | 手撸架构,Spring面试63问_vincent-CSDN博客 |
Spring cloud | 手撸架构,Spring cloud面试45问_vincent-CSDN博客 |
SpringBoot | 手撸面试,Spring Boot面试41问_vincent-CSDN博客 |
Netty 与 RPC | 手撸架构,Netty 与 RPC面试48问_vincent-CSDN博客 |
Doubo | 手撸架构,Dubbo面试49问_vincent-CSDN博客 |
Redis | 手撸架构,Redis面试41问_vincent-CSDN博客 |
Zookeeper | 手撸架构,Zookeeper面试27问_vincent-CSDN博客 |
Mysql | 手撸架构,Mysql 面试126问_vincent-CSDN博客 |
MyBatis | 手撸架构,MyBatis面试42问_vincent-CSDN博客 |
MongoDB | 手撸架构,MongDB 面试50问_vincent-CSDN博客 |
Elasticsearch | 手撸架构,Elasticsearch 面试25问_vincent-CSDN博客 |
RabbitMQ | 手撸架构,RabbitMQ 面试49问_vincent-CSDN博客 |
Kafka | 手撸架构,Kafka 面试42问_vincent-CSDN博客 |
Docker | 手撸架构,Docker 面试25问_vincent-CSDN博客 |
Nginx | 手撸架构,Nginx 面试40问_vincent-CSDN博客 |
算法 | 常用排序算法总结(1)-- 比较排序_vincent-CSDN博客_比较排序 常用排序算法总结(2)-- 非比较排序算法_vincent-CSDN博客_非比较排序的算法有 |
分布式事务 | 分布式事务解决方案(总览)_vincent-CSDN博客 |
HTTP | 太厉害了,终于有人能把TCP/IP 协议讲的明明白白了_vincent-CSDN博客_tcp和ip |
Apach Kafka 是一款分布式流处理框架,用于实时构建流处理应用。它有一个核心 的功能广为人知,即作为企业级的消息引擎被广泛使用。
(1).Kafka 把 topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定
期清除或删除已经消费完文件,减少磁盘占用。
(2).通过索引信息可以快速定位 message 和确定 response 的最大大小。
(3).通过 index 元数据全部映射到 memory,可以避免 segment file 的 IO 磁盘操作。
(4).通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。
(1).Kafka 持久化日志,这些日志可以被重复读取和无限期保留
(2).Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据
提升容错能力和高可用性
(3).Kafka 支持实时的流式处理
一种允许应用程序充当流处理器的 API,它还使用一个或多个主题的输入流,并生成一个输
出流到一个或多个输出主题,此外,有效地将输入流转换为输出流,我们称之为流 API。
连续、实时、并发和以逐记录方式处理数据的类型,我们称之为 Kafka 流处理。
1、定义:即消费者组是 Kafka 提供的可扩展且具有容错性的消费者机制。
2、原理:在 Kafka 中,消费者组是一个由多个消费者实例 构成的组。多个实例共同订阅若干个主题,实现共同消费。同一个组下的每个实例都配置有 相同的组 ID,被分配不同的订阅分区。当某个实例挂掉的时候,其他实例会自动地承担起 它负责消费的分区。
此时,又有一个小技巧给到你:消费者组的题目,能够帮你在某种程度上掌控下面的面试方
向。
bin/kafka-topics.sh --list --zookeeper localhost:2181
目前,Kafka 使用 ZooKeeper 存放集群元数据、成员管理、Controller 选举,以及其他一些管理类任务。之后,等 KIP-500 提案完成后,Kafka 将完全不再依赖 于 ZooKeeper。
“存放元数据”是指主题 分区的所有数据都保存在 ZooKeeper 中,且以它保存的数据为权威,其他“人”都要与它 保持对齐。“成员管理”是指 Broker 节点的注册、注销以及属性变更,等 等。“Controller 选举”是指选举集群 Controller,而其他管理类任务包括但不限于主题 删除、参数配置等。
不过,抛出 KIP-500 也可能是个双刃剑。碰到非常资深的面试官,他可能会进一步追问你 KIP-500 是做的。一言以蔽之:KIP-500 思想,是使用社区自研的基于 Raft 的共识算法, 替代 ZooKeeper,实现 Controller 自选举。
broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站。
在 Kafka 中,每个 主题分区下的每条消息都被赋予了一个唯一的 ID 数值,用于标识它在分区中的位置。这个 ID 数值,就被称为位移,或者叫偏移量。一旦消息被写入到分区日志,它的位移值将不能 被修改。
答完这些之后,你还可以把整个面试方向转移到你希望的地方。常见方法有以下 3 种:
(1)节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连
接
(2)如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久
Kafka 副本当前分为领导者副本和追随者副本。只有 Leader 副本才能 对外提供读写服务,响应 Clients 端的请求。Follower 副本只是采用拉(PULL)的方 式,被动地同步 Leader 副本中的数据,并且在 Leader 副本所在的 Broker 宕机后,随时 准备应聘 Leader 副本。
通常来说,回答到这个程度,其实才只说了 60%,因此,我建议你再回答两个额外的加分 项。
Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据,kafka使用ISR的方式很好的均衡了确保数据不丢失以及吞吐率。Follower可以批量的从Leader复制数据,而且Leader充分利用磁盘顺序读以及send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了Follower与Leader的消息量差。
kafka 如何减少数据丢失
Kafka到底会不会丢数据(data loss)? 通常不会,但有些情况下的确有可能会发生。下面的参数配置及Best practice列表可以较好地保证数据的持久性(当然是trade-off,牺牲了吞吐量)。
这道题除了要回答消费者端的参数设置之外,一定要加上 Broker 端的设置,这样才算完整。毕竟,如果 Producer 都不能向 Broker 端发送数据很大的消息,又何来消费一说呢? 因此,你需要同时设置 Broker 端参数和 Consumer 端参数。
Broker 端的最后一个参数比较容易遗漏。我们必须调整 Follower 副本能够接收的最大消 息的大小,否则,副本同步就会失败。因此,把这个答出来的话,就是一个加分项。
任何 Java 进程 JVM 堆大小的设置都需要仔细地进行考量和测 试。一个常见的做法是,以默认的初始 JVM 堆大小运行程序,当系统达到稳定状态后,手动触发一次 Full GC,然后通过 JVM 工具查看 GC 后的存活对象大小。之后,将堆大小设 置成存活对象总大小的 1.5~2 倍。对于 Kafka 而言,这个方法也是适用的。不过,业界有 个最佳实践,那就是将 Broker 的 Heap Size 固定为 6GB。经过很多公司的验证,这个大 小是足够且良好的。
这道题目考查的是机器数量和所用资源之间的关联关系。所谓资源,也就是 CPU、内存、磁盘和带宽。
通常来说,CPU 和内存资源的充足是比较容易保证的,因此,你需要从磁盘空间和带宽占用两个维度去评估机器数量。
在预估磁盘的占用时,你一定不要忘记计算副本同步的开销。如果一条消息占用 1KB 的磁 盘空间,那么,在有 3 个副本的主题中,你就需要 3KB 的总空间来保存这条消息。显式地 将这些考虑因素答出来,能够彰显你考虑问题的全面性,是一个难得的加分项。
对于评估带宽来说,常见的带宽有 1Gbps 和 10Gbps,但你要切记,这两个数字仅仅是最大值。因此,你最好和面试官确认一下给定的带宽是多少。然后,明确阐述出当带宽占用接 近总带宽的 90% 时,丢包情形就会发生。这样能显示出你的网络基本功。
在生产环境中,你一定碰到过“某个主题分区不能工作了”的情形。使用命令行查看状态的 话,会发现 Leader 是 -1,于是,你使用各种命令都无济于事,最后只能用“重启大 法”。
但是,有没有什么办法,可以不重启集群,就能解决此事呢?这就是此题的由来。
我直接给答案:删除 ZooKeeper 节点 /controller,触发 Controller 重选举。 Controller 重选举能够为所有主题分区重刷分区状态,可以有效解决因不一致导致的 Leader 不可用问题。我几乎可以断定,当面试官问出此题时,要么就是他真的不知道怎么 解决在向你寻求答案,要么他就是在等你说出这个答案。所以,千万别一上来就说“来个重 启”之类的话。
leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。
kafka在Broker端提供了一个配置参数:unclean.leader.election,这个参数有两个值:
true(默认):允许不同步副本成为leader,由于不同步副本的消息较为滞后,此时成为leader,可能会出现消息不一致的情况。
false:不允许不同步副本成为leader,此时如果发生ISR列表为空,会一直等待旧leader恢复,降低了可用性。
其实,Kafka 不需要用户手动删除消息。它本身提供了留存策略,能够自动删除过期消息。 当然,它是支持手动删除消息的。因此,你最好从这两个维度去回答。
这是一个内部主题,公开的官网资料很少涉及到。因此,我认为,此题属于面试官炫技一类 的题目。你要小心这里的考点:该主题有 3 个重要的知识点,你一定要全部答出来,才会显得对这块知识非常熟悉。
它是一个内部主题,无需手动干预,由 Kafka 自行管理。当然,我们可以创建该主题。
它的主要作用是负责注册消费者以及保存位移值。可能你对保存位移值的功能很熟悉, 但其实该主题也是保存消费者元数据的地方。千万记得把这一点也回答上。另外,这里 的消费者泛指消费者组和独立消费者,而不仅仅是消费者组。
Kafka 的 GroupCoordinator 组件提供对该主题完整的管理功能,包括该主题的创建、 写入、读取和 Leader 维护等。
分区的 Leader 副本选举对用户是完全透明的,它是由 Controller 独立完成的。你需要回答的是,在哪些场景下,需要执行分区 Leader 选举。每一种场景对应于一种选举策略。当前,Kafka 有 4 种分区 Leader 选举策略。
这 4 类选举策略的大致思想是类似的,即从 AR 中挑选首个在 ISR 中的副本,作为新 Leader。当然,个别策略有些微小差异。不过,回答到这种程度,应该足以应付面试官 了。毕竟,微小差别对选举 Leader 这件事的影响很小。
Zero Copy 是特别容易被问到的高阶题目。在 Kafka 中,体现 Zero Copy 使用场景的地方有两处:基于 mmap 的索引和日志文件读写所用的 TransportLayer。
先说第一个。索引都是基于 MappedByteBuffer 的,也就是让用户态和内核态共享内核态 的数据缓冲区,此时,数据不需要复制到用户态空间。不过,mmap 虽然避免了不必要的 拷贝,但不一定就能保证很高的性能。在不同的操作系统下,mmap 的创建和销毁成本可 能是不一样的。很高的创建和销毁开销会抵消 Zero Copy 带来的性能优势。由于这种不确 定性,在 Kafka 中,只有索引应用了 mmap,最核心的日志并未使用 mmap 机制。
再说第二个。TransportLayer 是 Kafka 传输层的接口。它的某个实现类使用了 FileChannel 的 transferTo 方法。该方法底层使用 sendfile 实现了 Zero Copy。对 Kafka 而言,如果 I/O 通道使用普通的 PLAINTEXT,那么,Kafka 就可以利用 Zero Copy 特 性,直接将页缓存中的数据发送到网卡的 Buffer 中,避免中间的多次拷贝。相反,如果 I/O 通道启用了 SSL,那么,Kafka 便无法利用 Zero Copy 特性了。
这道题目考察的是你对 Leader/Follower 模型的思考。
Leader/Follower 模型并没有规定 Follower 副本不可以对外提供读服务。很多框架都是允 许这么做的,只是 Kafka 最初为了避免不一致性的问题,而采用了让 Leader 统一提供服 务的方式。
不过,在开始回答这道题时,你可以率先亮出观点:自 Kafka 2.4 之后,Kafka 提供了有限度的读写分离,也就是说,Follower 副本能够对外提供读服务。
说完这些之后,你可以再给出之前的版本不支持读写分离的理由。
回答任何调优问题的第一步,就是确定优化目标,并且定量给出目标!这点特别重要。对于 Kafka 而言,常见的优化目标是吞吐量、延时、持久性和可用性。每一个方向的优化思路都 是不同的,甚至是相反的。
确定了目标之后,还要明确优化的维度。有些调优属于通用的优化思路,比如对操作系统、 JVM 等的优化;有些则是有针对性的,比如要优化 Kafka 的 TPS。我们需要从 3 个方向去考虑
这道题目能够诱发我们对分布式系统设计、CAP 理论、一致性等多方面的思考。不过,针 对故障定位和分析的这类问题,我建议你首先言明“实用至上”的观点,即不论怎么进行理论分析,永远都要以实际结果为准。一旦发生 Controller 网络分区,那么,第一要务就是 查看集群是否出现“脑裂”,即同时出现两个甚至是多个 Controller 组件。这可以根据 Broker 端监控指标 ActiveControllerCount 来判断。
现在,我们分析下,一旦出现这种情况,Kafka 会怎么样。
由于 Controller 会给 Broker 发送 3 类请求,即LeaderAndIsrRequest、 StopReplicaRequest 和 UpdateMetadataRequest,因此,一旦出现网络分区,这些请求将不能顺利到达 Broker 端。这将影响主题的创建、修改、删除操作的信息同步,表现为 集群仿佛僵住了一样,无法感知到后面的所有操作。因此,网络分区通常都是非常严重的问 题,要赶快修复。
在回答之前,如果先把这句话说出来,一定会加分:Java Consumer 是双线程的设计。一 个线程是用户主线程,负责获取消息;另一个线程是心跳线程,负责向 Kafka 汇报消费者 存活情况。将心跳单独放入专属的线程,能够有效地规避因消息处理速度慢而被视为下线 的“假死”情况。
单线程获取消息的设计能够避免阻塞式的消息获取方式。单线程轮询方式容易实现异步非阻塞式,这样便于将消费者扩展成支持实时流处理的操作算子。因为很多实时流处理操作算子都不能是阻塞式的。另外一个可能的好处是,可以简化代码的开发。多线程交互的代码是非常容易出错的。
首先,Follower 发送 FETCH 请求给 Leader。
接着,Leader 会读取底层日志文件中的消 息数据,再更新它内存中的 Follower 副本的 LEO 值,更新为 FETCH 请求中的 fetchOffset 值。最后,尝试更新分区高水位值。Follower 接收到 FETCH 响应之后,会把 消息写入到底层日志,接着更新 LEO 和 HW 值。
Leader 和 Follower 的 HW 值更新时机是不同的,Follower 的 HW 更新永远落后于 Leader 的 HW。这种时间上的错配是造成各种不一致的原因。
要确定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之后再确认消息发送成功;异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态;
针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。
kafka每个partition中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的。
整个topic不保证有序。如果为了保证topic整个有序,那么将partition调整为1.
offset+1
topic 中的多个 partition 以文件夹的形式保存到 broker,每个分区序号从 0 递增,且消息有序。Partition 文件下有多个 segment(xxx.index,xxx.log),segment 文件里的大小和配置文件大小一致可以根据要求修改默认为 1g。如果大小大于 1g 时,会滚动一个新的 segment 并且以上一个 segment 最后一条消息的偏移量命名。
消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和 CRC32校验码。
大部分消息系统在 broker 端的维护消息被消费的记录:一个消息被分发到consumer 后 broker 就马上进行标记或者等待 customer 的通知后进行标记。这样也可以在消息在消费后立马就删除以减少空间占用。
但是这样会不会有什么问题呢?如果一条消息发送出去之后就立即被标记为消费过的,旦 consumer 处理消息时失败了(比如程序崩溃)消息就丢失了。为了解决这个问题,很多消息系统提供了另外一个个功能:当消息被发送出去之后仅仅被标记为已发送状态,当接到 consumer 已经消费成功的通知后才标记为已被消费的状态。这虽然解决了消息丢失的问题,但产生了新问题,首先如果 consumer处理消息成功了但是向 broker 发送响应时失败了,这条消息将被消费两次。第二个问题时,broker 必须维护每条消息的状态,并且每次都要先锁住消息然后更改状态然后释放锁。这样麻烦又来了,且不说要维护大量的状态数据,比如如果消息发送出去但没有收到消费成功的通知,这条消息将一直处于被锁定的状态,Kafka 采用了不同的策略。Topic 被分成了若干分区,每个分区在同一时间只被一个 consumer 消费。这意味着每个分区被消费的消息在日志中的位置仅仅是一个简单的整数:offset。这样就很容易标记每个分区消费状态就很容易了,仅仅需要一个整数而已。这样消费状态的跟踪就很简单了。
这带来了另外一个好处:consumer 可以把 offset 调成一个较老的值,去重新消费老的消息。这对传统的消息系统来说看起来有些不可思议,但确实是非常有用的,谁规定了一条消息只能被消费一次呢?