关于Kafka面试题整理

简介
  • Apache Kafka是一个分布式发布 - 订阅消息系统和一个强大的队列, 可以处理大量的数据, 并使您 能够将消息从一个端点传递到另一个端点. Kafka适合离线和在线消息消费. Kafka消息保留在磁盘 上, 并在群集内复制以防止数据丢失. Kafka构建在ZooKeeper同步服务之上. 它与Apache Storm 和Spark非常好地集成, 用于实时流式数据分析. Kafka 依赖于日志顺序写, 因此支持消息回溯和支撑高性能读写 依赖 Zookeeper
架构
                                     
                                            
基础概念
Broker
  • Server. 包含多个 Topic , Partition, Replica. 负责协调 Producer Consumer
  • 主从结构为: 主节点为 Controller, 从节点为从节点 Kafka 启动是会往 Zookeeper 中注册当前 Broker 信息. 谁先注册谁就是 Controller. 读取注册上来的从节点的数据(通过监听机制), 生成集群 的元数据信息, 之后把这些信息都分发给其他的服务器, 让其他服务器能感知到集群中其它成员的 存在
Topic
  • 标准 MQ 中的 Queue. Kafka 中一个 Topic 的消息会保存在不同的 Partition (不同的 Broker)来保 证高可用
                                              
Partition ( 分区 )
  • 可以理解为将标准 MQ Queue 的消息进行拆分, 来实现高可用
  • Producer 发送的 Message, 根据 key partition 数进行 hash, 然后进行投递
  • 一个分区只能被同一个 Consumer Group 中的一个 Consumer 消费. 分区内消费有序

                       

Replica ( 备份 )
  • 每一个 Partition 的备份. Replica 的小于等于 Broker 的数量
  • Leader: Replica领导节点, 每一个 Partition 都有对应的 Leader 节点(Broker). Producer 写数据 时, 只会往 Leader 中写. Consumer 读数据也是从 Leader 中读 Follower: Replica跟随节点, 用于 复制领导节点的数据. 复制 Leader 消息采用 pull ()模式
 
# Broker 设置副本数量 默认为 3
default.replication.factor
# Topic 设置副本数量
replication-factor
ISR (In-Sync Replica)
  • Leader维护一个与其基本保持同步的Replica列表, 每个Partition都会有一个ISR, 而且是由leader 动态维护. 如果一个flflower比一个leader落后太多, 或者超过一定时间未发起数据复制请求, 则 leader将其重ISR中移除. ISR中所有Replica都向Leader发送ACK, leadercommit
  • Leader 宕机之后, 会从 ISR 选择数据最新的 Follower 来当做 Leader 如果 ISR 全部宕机, 则选择第 一个回复的 Replica 当做 Leader 节点 (消息可能会丢失或者重复消费)
rerplica.lag.time.max.ms=10000
# 如果leader发现flower超过10秒没有向它发起fech请求, 那么leader考虑这个flower是不是程
序出了点问题
# 或者资源紧张调度不过来, 它太慢了, 不希望它拖慢后面的进度, 就把它从ISR中移除.
rerplica.lag.max.messages=4000
# 相差4000条就移除
# flower慢的时候, 保证高可用性, 同时满足这两个条件后又加入ISR中,
# 在可用性与一致性做了动态平衡 亮点
min.insync.replicas=1
# 需要保证ISR中至少有多少个replica
水印备份机制
  • LEO (last end offffset): 日志末端位移, 记录了该副本对象底层日志文件中下一条消息的位移值, 副本写入消息的时候, 会自动更新 LEO Leader 会保存两个 LEO , 一个是自己的 LEO , 另外一 个是 remote LEO . Follower 每次 fetch 请求都会携带当前 LEO, Leader 会选择最小的 LEO 来更新 HW
  • HW (high watermark): 从名字可以知道, 该值叫高水印值, HW 一定不会大于 LEO , 小于 HW 值 的消息被认为是"已提交""已备份"的消息, 并对消费者可
                                            
                                                 
Message
  • 标准 MQ Queue 中的 Message. 即一条消息
Producer
  • 标准 MQ 中的发送方. 发送给 Broker 使用push ()模式
数据一致性保证 ( 消息不丢失 )
request.required.asks=0
# 0:相当于异步的, 不需要leader给予回复, producer立即返回, 发送就是成功,
那么发送消息网络超时或broker crash(1.Partition的Leader还没有commit消息 2.Leader与
Follower数据不同步), 既有可能丢失也可能会重发
# 1:当leader接收到消息之后发送ack, 丢会重发, 丢的概率很小
# -1:当所有的follower都同步消息成功后发送ack. 不会丢失消息
Consumer
  • 标准 MQ 中的消费方. 接受 Broker 使用 pull ()模式, 默认 100ms 拉一次. Consumer 消费的是 Partition 的数据
  • 消息丢失: 手动确认 ack 而不是自动提交 消息重复: 消费端幂等处理
Consumer Group
  • Kafka , 一个 Topic 是可以被一个消费组消费, 一个Topic 分发给 Consumer Group 中的 Consumer 进行消费, 保证同一条 Message 不会被不同的 Consumer 消费
  • 注意: Consumer Group Consumer 数量大于 Partition 的数量时, 超过 Partition 的数量将会 拿不到消息
分片规则
  • Kafka分配Replica的算法有两种: RangeAssignor RoundRobinAssignor 默认为RangeAssignor:
  1. 将所有Broker(假设共nBroker)和待分配的Partition排序
  2.  将第iPartition分配到第(i mod n)Broker
  3.  将第iPartition的第jReplica分配到第((i + j) mod n)Broker
Rebalance ( 重平衡 )
  • Group Coordinator 是一个服务, 每个 Broker 在启动的时候都会启动一个该服务 Group
  • Coordinator 的作用是用来存储 Group 的相关 Meta 信息, 并将对应 Partition Offffset 信息记录 到 Kafka 内置 Topic(__consumer_offffsets) Kafka 0.9之前是基于 Zookeeper 来存储Partition 的 offffset 信息(consumers/{group}/offffsets/{topic}/{partition}), 因为 Zookeeper 并不适用于频 繁的写操作, 所以在0.9之后通过内置 Topic 的方式来记录对应 Partition offffset
触发条件
 1. 组成员个数发生变化
          1. 新的消费者加入到消费组
          2. 消费者主动退出消费组
         3. 消费者被动下线 . 比如消费者长时间的 GC, 网络延迟导致消费者长时间未向 Group Coordinator发送心跳请求 , 均会认为该消费者已经下线并踢出
 2. 订阅的 Topic Consumer Group 个数发生变化
 3. Topic 的分区数发生变化
Rebalace 流程
 
  Rebalance 过程分为两步: Join Sync
  1.   Join: 顾名思义就是加入组. 这一步中, 所有成员都向 Coordinator 发送 JoinGroup 请求, 请求 加入消费组. 一旦所有成员都发送了 JoinGroup 请求, Coordinator 会从中选择一个 Consumer 担任 Leader 的角色, 并把组成员信息以及订阅信息发给 Consumer Leader 注意 Consumer Leader 和 Coordinator不是一个概念. Consumer Leader负责消费分配方案的制定
  2.   Sync: Consumer Leader 开始分配消费方案, 即哪个 Consumer 负责消费哪些 Topic 的哪些 Partition. 一旦完成分配, Leader 会将这个方案封装进 SyncGroup 请求中发给 Coordinator, 非 Leader 也会发 SyncGroup 请求, 只是内容为空. Coordinator 接收到分配方案之后会把方 案塞进SyncGroupResponse中发给各个Consumer. 这样组内的所有成员就都知道自己应 该消费哪些分区了

                                                          

如何避免 Rebalance
  • 对于触发条件的 2 3, 我们可以人为避免. 1 中的 1 3 人为也可以尽量避免, 主要核心为 3
# 心跳相关
session.timeout.ms = 6s
heartbeat.interval.ms = 2s
# 消费时间
max.poll.interval.ms
日志索引
  • Kafka 能支撑 TB 级别数据, 在日志级别有两个原因: 顺序写和日志索引. 顺序写后续会讲
  • Kafka 在一个日志文件达到一定数据量 (1G) 之后, 会生成新的日志文件, 大数据情况下会有多个日 志文件, 通过偏移量来确定到某行纪录时, 如果遍历所有的日志文件, 那效率自然是很差的. Kafka在日志级别上抽出来一层日志索引, 来方便根据 offffset 快速定位到是某个日志文件
  • 每一个 partition 对应多个个 log 文件(最大 1G), 每一个 log 文件又对应一个 index 文件
通过 offffset 查找 Message 流程 :
  • 通过二分(368773 - 368769 = 4)定位到 index 文件 (368769.index) 中最大 小于等于该 offffset 的 对于的 log 文件偏移量(3, 497)
  • 通过定位到该文件的消息行(3, 497), 然后在往后一行一行匹配揭露(368773 830)
  • 先根据 offffset (: 368773), 二分定位到最大 小于等于该 offffset index 文件 (368769.index)
                                                      
高性能 , 高吞吐
分区的原因
  • 如果我们假设像标准 MQ Queue, 为了保证一个消息只会被一个消费者消费, 那么我们第一想到 的就是加锁. 对于发送者, 在多线程并且非顺序写环境下, 保证数据一致性, 我们同样也要加锁. 一旦 考虑到加锁, 就会极大的影响性能. 我们再来看Kafka Partition, Kafka 的消费模式和发送模式都 是以 Partition 为分界. 也就是说对于一个 Topic 的并发量限制在于有多少个 Partition, 就能支撑 多少的并发. 可以参考 Java1.7 ConcurrentHashMap 的桶设计, 原理一样, 有多少桶, 支持多少 的并发
顺序写
  • 从图中可以看出来, 磁盘的顺序写的性能要比内存随机写的还要强. 磁盘顺序写和随机写的差距也是天壤之别
                   
批发送
  • 批处理是一种常用的用于提高I/O性能的方式. Kafka而言, 批处理既减少了网络传输的 Overhead, 又提高了写磁盘的效率. Kafka 0.82 之后是将多个消息合并之后再发送, 而并不是send 一条就立马发送(之前支持)
# 批量发送的基本单位, 默认是16384Bytes, 即16kB
batch.size
# 延迟时间
linger.ms
# 两者满足其一便发送
数据压缩
  • 数据压缩的一个基本原理是, 重复数据越多压缩效果越好. 因此将整个Batch的数据一起压缩能更大 幅度减小数据量, 从而更大程度提高网络传输效率
  • Broker接收消息后,并不直接解压缩,而是直接将消息以压缩后的形式持久化到磁盘 Consumer接受到压缩后的数据再解压缩
  • 整体来讲: Producer Broker, 副本复制, Broker Consumer 的数据都是压缩后的数据, 保证高 效率的传输
Page Cache & MMap
  • 利用 MMap 的读写文件也会依赖于 Page Cache, Page Cache 可以单独存在, 但是 MMap 会依赖 于 Page Cache. MMap 是将文件映射到内存中, 底层来讲就是将 Page Cache 映射到用户空间, 从 而使得用户空间可以直接操作文件
Page Cache
  • 内核会为每个文件单独维护一个page cache, 用户进程对于文件的大多数读写操作会直接作用到 page cache上, 内核会选择在适当的时候将page cache中的内容写到磁盘上 (当然我们可以手工 fsync控制回写), 这样可以大大减少磁盘的访问次数, 从而提高性能
  • 至于刷盘时机: page cache中的数据会随着内核中flflusher线程的调度以及对sync()/fsync()的调用 写回到磁盘, 同样也可以通过手动调用写磁盘
MMap (Memory Mapped Files, 内存映射文件 )
  • MMap 是将一个文件或者其它对象映射进内存. Java 支持的零拷贝参考MappedByteBuffffer
正常的写流程 :
  1.  JVMOS发出read()系统调用, 触发上下文切换, 从用户态切换到内核态
  2.  从外部存储 (如硬盘)读取文件内容通过直接内存访问(DMA)存入内核地址空间的缓冲区
  3.  将数据从内核缓冲区拷贝到用户空间缓冲区, read()系统调用返回, 并从内核态切换回用户态
  4.  JVMOS发出write()系统调用, 触发上下文切换, 从用户态切换到内核态
  5.  将数据从用户缓冲区拷贝到内核中与目的地Socket关联的缓冲区
  6.  数据最终经由Socket通过DMA传送到硬件(如网卡)缓冲区, write()系统调用返回, 并从内核态切换回用户态
   读取 : 用户态切换到内核态 , 在内核空间将文件读取到缓冲区 , 再从内核区拷贝到用户缓冲区 , 最后 切换回用户态 写入: 用户态切换到内核态 , 将数据从用户缓冲区拷贝到内核缓冲区 , 再由 DMA 发送 到硬件缓冲区, 最后切换回用户态
   我们发现 , 其实用户空间和内核空间的切换一共四次 , 文件拷贝四次 . 如果有一种方式文件
                      
mmap 的写流程
  1.  JVMOS发出sendfifile()系统调用
  2.  从外部存储 (如硬盘)读取文件内容通过直接内存访问(DMA)存入内核地址空间的缓冲区
  3.  数据最终经由Socket通过DMA传送到硬件(如网卡)缓冲区, write()系统调用返回, 并从内核态切换回用户态
通过零拷贝 , 我们减少了两次用户空间和内核空间的拷贝和一次内核空间和 Socket 缓冲区的拷贝 , 总共节省三次拷贝, 并且没有用户态和内核态的转换
Kafka 的运用
  • Kafka 写数据的时候, 会直接写到 Page Cache , 消费者拉数据时也会经过 Page Cache. 如果 Kafka 的写速率和消费速率差不多, 那么整个生产和消费过程是不会经过磁盘 IO. 全部都是内存操作. 对于 Page Cache 没有刷盘导致的数据丢失, 如果发送方配置消息至少有一个副本接受, 那么也 只会有已一次同步刷盘
Kafka 为什么不自己管理缓存 , 而非要用 page cache?
  1.  JVM中一切皆对象, 数据的对象存储会带来所谓 object overhead 浪费空间
  2. 如果由JVM来管理缓存, 会受到GC的影响, 并且过大的堆也会拖累GC的效率, 降低吞吐量
  3.  一旦程序崩溃, 自己管理的缓存数据会全部丢失
                       
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值