Kafka
前几天谈完绩效,问今年有没有涨薪。组长的原话是“很难。。。我尽力帮你争取。。。"我刚听完脑海中的第一个想法:“这里加薪难,不难!”。
冷静分析一波,今年整体环境不好,还是活到年底,不要准备跳,和你分享之前准备好的kafka相关知识点
,看机会的时候可以复习复习。kafka也是面试常考的组成部分,一些基本概念不再写,所以在面试中写一些常问的点。
Broker
:通俗地说,部署kafka的服务器是broker,kafka集群由多个broker组成,每个broker包含多个topicController
:broker的领导者,主写主读,负责管理整个集群中所有分区和副本的状态Producer
:消息制作人决定将数据发送到哪个partaion,hash或轮询Consumer
:消息消费者通过zookeper维护ofsetetConsumer Group
:消费者群体,同一群体中不同的消费者负责消费不同的派对,即一个分区只能由一个群体中的消费者消费;消费者群体之间没有相互影响。每个消息只能由Consumer发送 一个Consumer在Group中消费;但是,它可以被多个Consumer消费 Group组消费。Topic
:新闻主题,一类新闻的总称/新闻团队,逻辑概念,真实数据存储在partation中,一个 topic 由多个 partions 组成Partation
:一个Topic可以分为多个Partition,物理存储在Kafka集群中的多个Broker上。考虑到可靠性,每个Partition都有备份Replica。Partion保持分区顺序Replica副本
:为了保证集群中某个节点出现故障,Partition上的Partition数据不会丢失,Kafka可以继续工作,所以Kafka提供了一个副本机制,每个Partion都有几个副本,一个Leader和几个Follower。Leader
:Replica的主角,Producer和Consumer只与Leader互动。Follwer
:Replica实时从角色和Leader中同步数据,并与Leader数据保持同步。当Leader出现故障时,经过一系列选举算法,Follower将成为一个新的Leader。Offset
:每个分区日志都有一个offset来标记唯一的信息。offset的值是8个字节的数字,这意味着这个消息在partition中的起始位置
- 整体结构分为producerer、broker、consumer三部分,3.0版本之前依靠zookeper进行集群管理,3.0版本后通过kraft进行集群管理。
- consumer有消费者群体的概念,同一群体中不同的消费者负责消费不同的partation,一个分区只能由一个群体中的消费者消费;消费者群体不相互影响
- 集群中的broker将选择leader作为controller管理整个集群中所有分区和副本的状态
- 每个topic由多个partation组成,
partation是真实存储数据的地方,每个partation以文件夹的形式存储在文件系统中。存储每个相应的partation数据目录*.index,*log,*timeindex三个文件
- 每个partation都有相应的副本,分散在不同的broker中实现分布式存储。
- 整体采用主写主读架构,通过partation分布不同的broker,尽量保证每个broker都有replicas分区拉数据和leader分区生产数据,实现负载
- 为了保证数据的安全性,kafka在producer写入数据时,会通过副本机制复制备份当前数据,其他分区的副本会通过拉取同步数据,依靠多个副本机制转移故障。
- HW: 高水位,标识consumer可见的offset,取所有ISR中最小的,只有所有副本同步完成HW才会增加,消费者只能消耗HW后的数据
- LEO: 每个partationlog的最后一个message位置
- AR: 所有分区副本集合
- ISR: 同步分区集合队列,属于AR子集,如果ISR中同步慢或挂起,就会T出ISR队列。
- OSR:从同步队列中提出的分区集合,
- partation 挂断leader后,controller在ISR集合中找到了第一个选举新leader
- Producer保证发送数据不丢失,生产者发送消息有三种模式,
发完就忘了
、同步
和异步
,响应结果可以通过设置同步或异步来获得,失败的重试可以保证消息在发送阶段不会丢失(broker接受produer数据作为功率保证) - Broker保证接收数据不丢失,当生产者向leader发送数据时,通过requesttt.required.设置数据可靠性等级的acks参数。
- 1(默认): ISR中producer的leader已成功收到数据,并在确认后发送下一个message。如果leader停机,数据将丢失。
- 0:producer继续发送下一批消息,而无需等待broker的确认。在这种情况下,数据传输效率最高,但数据可靠性确实最低。
- -1或者all:producer需要等待ISR中的所有follower确认收到数据后才能一次发送,可靠性最高。通过设置ack=1,broker内部副本同步确保broker内部数据不丢失。
- Consumer确保消费数据不会丢失。默认情况下,当消费者消费消息时,他们会自动提交offse。但是,如果消费者消费出错,不进入真正的业务处理,可能会导致消息消费失败,从而丢失。您可以在提交offset之前打开手动提交位移,等待业务正常处理。
- kafka通过异步发送机制生产消息时,首先通过main线程缓存数据,sender线程批量处理数据,broker定期访问poll数据。
- 批量读写数据,批量压缩消息发送到broker之前会压缩消息,达到一定数据量的压缩一次性发送。
- 顺序写磁盘:将新消息顺序添加到日志文件的末尾,磁盘上 数据不会一直存在,后台会维护一个线程 定期检测是否有数据应该删除。
- PageCache页面缓存:充分利用Linux操作系统优化磁盘的访问,Cache层在内存中缓存磁盘上的部分数据。(类似mysql的bufferpol)broker在收到数据后将生产者的数据写入page cache,然后定期刷入磁盘
- 零拷贝技术:通过 NIO 的 transferTo/transferFrom 调用操作系统 sendfile 实现零拷贝(高频考点)。
- 数据分区分段 + 稀疏索引:Kafka 的 message 新闻实际上是分布式存储在小segment中,每个文件操作也是直接操作的 segment。为进一步查询优化,Kafka 默认情况下,索引文件建立在分段后的数据文件中,即文件系统上 .index文件。分段+索引的设计不仅提高了数据读取的效率,而且提高了数据操作的并行性。
- partation是真实存储数据的地方,每个partation以文件夹的形式存储在文件系统中。每个partation文件夹中的每个对应的日志被分成许多segment段。
- 日志分段名通过偏移量确定。比如segment1的段号是509,segment2的段号是1397,那么segment1就存储了509-1397偏移量的消息。
- 通过稀疏索引定位到段后,即使用*.index文件。之所以成为稀疏索引,是因为没有维护所有数据的索引。定位数据时,需要通过两点搜索定位索引的位置,然后通过索引对应的真实数据的位置回表查询。
- *.timeindex 它与kafka清理数据密切相关。kafka默认保留7天内的数据。7天以上的数据将被清理干净。这里的清理逻辑主要根据timeindex时间索引文件中的最大时间来判断。如果最大时间与当前时间之间的差值超过7天,则将清理相应的数据段
- consumer group由多个消费者组成,他们一起消费 topic 所有的消息,和一个 topic 的一个 partition 只能被一个 consumer 消费。reblance是为了优化kafka对提高消费效率的优化,规定了所有consumergroup下的consumer均匀分配订阅 Topic 每个分区。
- 触发时机:①新consumer加入consumer group ②consumer在组内离开或崩溃
- 触发原因:生产环境中rebalance现象的大部分原因是
消费者心跳超时
、消费者消费数据超时
- 主要参数:
- session.timeout.ms 表示 consumer 向 broker 超时发送心跳。例如 session.timeout.ms = 180000 表示在最长 180 秒内 broker 没收到 consumer 心跳,所以 broker 就认为该 consumer 死了,就会开始 rebalance。
- heartbeat.interval.msheart 表示 consumer 每次向 broker 发送心跳的时间间隔。heartbeat.interval.ms = 60000 表示 consumer 每 60 秒向 broker 发送心跳。一般来说,session.timeout.ms 的值是 heartbeat.interval.ms 值的 3 倍以上。
- max.poll.interval.ms
- max.poll.records 这意味着你每次消费都会得到多少条信息。获得的消息越多,处理时间就越长。因此,每次提取的消息数量不能太多,需要确保 max.poll.interval.ms 可以在设定的时间内消费,否则会发生 rebalance。
- 解决方案:
- 超时心跳调整session.timeout.ms和heartbeat.interval.ms.
- 超时消费处理通常会增加消费者处理的时间(max.poll.interval.ms),减少每次处理的消息数量(max.poll.records)
- 可以考虑增加 topic 同时增加消费群体的消费者数量,消费者数量=分区数。
- 如果消费者消费不及时,可以采用多线程消费,优化业务方法流程,分区数相同,查看为什么并发性这么高。
- 刚才提到kafka 随着topic数量的不断增加,每个topic的分区数量将不一致,最终topic分区将在kafka集群中分布不均匀。
- 比如topic1是10个分区,topic2是15个分区,topic3是3个分区,集群中有6台机器。总有4个broker在6个broker上有两个topic1分区,3个broke上有3个topic3分区等等。这将导致分区较多的broker上的出入流量可能高于其他broker,最终导致资源问题。
- 在这种情况下,如果只添加新的broker扩展知识,它将不起作用。手动编辑内置副本迁移脚本
vi topic-reassignment.json
每个broker与partation的关系都是手动调整的。当然,网上也有很多自动迁移工具。 - 最近流行的pulsar自然支持动态伸缩能力,所以没必要这么努力。
- 作为主写,kafka不支持读写分离
- 读写分离本质上通过另一个节点分担主节点的负载压力,而kafka有一个独特的副本机制来实现负载功能
- 在一定条件下,分区数与吞吐量成正比,分区数与性能成正比。
- 超过一定限度,客户端和服务端需要使用的内存会激增
- 在许多组件中,服务端都保持了分区级别的缓存,分区越大,缓存成本就越大。
- 消费者端的消费线程数与分区数挂钩,分区数越大,消费线程数越多,线程成本越大
- 生产者发送消息有缓存的概念,每个分区都会缓存消息。当消息积累到一定程度或时间时,会发送到分区。分区越多,这部分的缓存就越大
- 文件句柄的费用,partation底层存储相应的log文件,文件句柄的数量增加
- 增加数据同步负担,降低高可用性