Kafka高频面试题及参考答案(京东网易知乎等多家面经汇总)

介绍下 Kafka,Kafka 的作用、组件、适用场景分别是什么?作为消息队列,它可解决什么样的问题?

Kafka 是一个 分布式流处理平台,最初由 LinkedIn 开发,后成为 Apache 顶级项目。它以 高吞吐量、低延迟、可扩展性 为核心优势,专为处理实时数据流而设计。

核心作用

  1. 消息系统:作为 消息中间件,支持生产者和消费者之间的异步通信。
  2. 数据管道:用于构建 实时数据集成管道,例如将数据从数据库同步到数据仓库。
  3. 流处理:结合流处理框架(如 Kafka Streams、Flink),实现实时数据处理和分析。

核心组件

Producer

向 Kafka 发布数据的客户端,负责将数据写入指定 Topic。

Broker

Kafka 集群中的单个节点,负责存储数据、处理读写请求。

Topic

逻辑上的数据分类,生产者按 Topic 发送数据,消费者按 Topic 订阅数据。

Partition

Topic 的物理分片,每个 Partition 是有序、不可变的消息序列,支持并行处理。

Consumer Group

一组消费者共同消费一个 Topic,每个 Partition 只能被组内一个消费者消费。

Zookeeper

早期版本用于管理集群元数据、Broker 注册、Leader 选举,新版已逐步移除依赖。

适用场景

  • 日志收集:集中收集分布式系统的日志数据(如 ELK 架构)。
  • 实时指标:监控系统实时生成指标(如用户行为跟踪)。
  • 事件溯源:记录系统状态变更事件(如电商订单状态流转)。
  • 流处理:实时处理点击流、传感器数据等。

解决的问题

  • 系统解耦:生产者和消费者无需直接通信,降低依赖性。
  • 削峰填谷:缓冲突发流量,避免下游系统过载。
  • 顺序保证:通过 Partition 内消息顺序性,支持需要严格顺序的业务场景。
  • 水平扩展:通过增加 Partition 和 Broker,轻松应对数据量增长。

说下 Kafka 架构、特点、优缺点,与其它消息组件相比有什么好处?

架构概览

Kafka 采用 分布式发布-订阅模型,核心架构包括:

  1. 生产者集群:向多个 Topic 推送数据。
  2. Broker 集群:每个 Broker 存储部分 Partition 数据,通过副本机制保障高可用。
  3. 消费者集群:以 Consumer Group 形式消费数据,支持水平扩展。

核心特点

  • 高吞吐:单机可支持每秒百万级消息写入(依赖硬件配置)。
  • 持久化存储:数据按需保留到磁盘,支持 TB 级数据累积。
  • 水平扩展:通过增加 Partition 和 Broker 实现扩容。
  • 多副本机制:每个 Partition 有多个副本,确保数据不丢失。

优缺点对比

高吞吐量和低延迟

单条消息延迟可能不如内存队列(如 Redis)

数据持久化能力强

配置复杂(如副本数、ISR 机制)

支持海量数据堆积

消费者需要自行管理 Offset(旧版本)

与其他消息组件的对比

RabbitMQ

适合复杂路由、事务消息,但吞吐量低于 Kafka。

ActiveMQ

支持 JMS 规范,但扩展性和吞吐量较弱。

RocketMQ

阿里系产品,功能更贴近 Kafka,但在事务消息上更成熟。

核心优势

  • 持久化与吞吐量:适合需要长期存储、高吞吐的场景。
  • 流式处理生态:与 Flink、Spark Streaming 等深度集成。

阐述 Kafka 生产者与消费者相关概念,如分区容错性、消费端的数据一致性、leader 挂掉之后处理方法

生产者相关

  • 分区策略:生产者决定将消息发送到 Topic 的哪个 Partition。常见策略包括:轮询(默认):均匀分配消息。Key Hash:按消息 Key 的哈希值分配到固定 Partition,保证相同 Key 的消息顺序性。
  • 容错性:通过 多副本机制 实现。每个 Partition 有多个副本(Leader 和 Follower),Leader 负责读写,Follower 同步数据。

消费者相关

  • 数据一致性:At Most Once:消息可能丢失,但不会重复。At Least Once:消息可能重复,但不会丢失(默认模式)。Exactly Once:通过事务机制保证消息仅处理一次(需 Kafka 0.11+)。
  • Offset 管理:消费者需定期提交 Offset(消费位置),若提交失败可能导致重复消费。

Leader 故障处理

  1. 检测故障:Zookeeper 或 Controller 监控 Broker 状态。
  2. 选举新 Leader:从 ISR(同步副本列表)中选择新的 Leader。
  3. 恢复服务:生产者/消费者自动切换到新 Leader,短暂不可用(通常毫秒级)。

说下 Kafka 的 ISR 机制、选举机制,ISR、OSR 和 ACK 分别是什么,ACK 有几种值?

ISR 机制

  • ISR(In-Sync Replicas):与 Leader 数据同步的副本集合。
  • OSR(Out-of-Sync Replicas):滞后于 Leader 的副本,可能因网络或宕机脱离 ISR。
  • AR(Assigned Replicas):所有副本(ISR + OSR)。

触发条件

  • Follower 副本与 Leader 的 消息差值时间差值 超过阈值时,移出 ISR。

选举机制

  • Controller 角色:某个 Broker 被选举为 Controller,负责 Partition 的 Leader 选举。
  • 优先副本选举:优先选择 ISR 中的第一个副本作为 Leader,减少数据不一致风险。

ACK 参数

ACK 表示生产者需要收到的确认信号:

  • 0:无需确认,可能丢失数据。
  • 1:Leader 写入成功即确认(默认)。
  • all(-1):所有 ISR 副本写入成功才确认,数据最安全。

Kafka 的工作原理是什么?如何保证数据不丢失、不重复?

工作原理

  1. 生产者推送:消息按分区策略发送到 Broker 的 Leader Partition。
  2. 副本同步:Leader 将数据复制到 Follower(ISR 内)。
  3. 消费者拉取:消费者从 Broker 拉取消息,按 Offset 顺序处理。

数据不丢失

  • 生产者端: 设置 acks=all,确保所有 ISR 副本写入成功。启用重试机制(retries=MAX_VALUE)。
  • Broker 端: 多副本机制,避免单点故障。定期刷盘(flush.messages 和 flush.ms 参数)。
  • 消费者端: 关闭自动提交 Offset,处理完消息再手动提交。

数据不重复

  • 生产者幂等性: 启用 enable.idempotence=true,通过唯一 ID 和序列号去重。
  • 消费者端: 实现业务逻辑幂等(如数据库唯一键)。使用 Kafka 事务(需配合 isolation.level=read_committed)。

Kafka 分区策略有哪些?如何尽可能保证数据可靠性?数据丢失怎么处理?如何保证全局有序?

分区策略是生产者决定将消息写入哪个分区的核心逻辑,直接影响数据分布和系统性能。常见的策略包括:

  • 轮询策略:默认策略,消息依次分配到不同分区,确保负载均衡。
  • 哈希策略:根据消息 Key 的哈希值分配到特定分区,保证相同 Key 的消息进入同一分区。
  • 自定义策略:业务可自行实现分区逻辑(如按地理位置分配)。

保证数据可靠性

  1. 多副本机制:每个分区设置多个副本(如 replication factor=3),数据写入 Leader 后同步到 Follower。
  2. ACK 确认机制:生产者设置 acks=all,确保所有 ISR 副本写入成功后才返回确认。
  3. 最小同步副本数:通过 min.insync.replicas 参数控制 ISR 最小副本数,避免单点故障导致数据丢失。

数据丢失处理

  • 生产者端: 启用重试机制(retries=Integer.MAX_VALUE),避免因网络抖动导致消息未发送。使用幂等生产者(enable.idempotence=true),防止重复发送。
  • Broker 端: 定期检查副本同步状态,及时剔除故障副本并重新选举 Leader。配置 unclean.leader.election.enable=false,禁止非 ISR 副本成为 Leader。
  • 消费者端: 手动提交 Offset,确保消息处理完成后再提交,避免消息丢失。

全局有序性

Kafka 仅保证分区内有序,全局有序需通过以下方式实现:

  • 单分区写入:所有消息写入同一分区,牺牲并行性。
  • 业务层排序:消费者拉取多个分区数据后按时间戳或序列号排序(可能引入延迟)。

生产者消费者模式与发布订阅模式在 Kafka 中的异同点是什么?Kafka 的消费者组是如何消费数据的?

模式对比

生产者-消费者(队列)

消息被一个消费者处理,适用于任务分发。

同一消费者组内多个消费者共享一个 Topic,每个 Partition 仅由一个消费者处理。

发布-订阅

消息广播给所有订阅者,适用于日志分发。

不同消费者组独立消费同一 Topic,每个组获取完整数据副本。

核心差异

  • 消费者组的存在使得 Kafka 可以灵活切换模式: 单消费者组 → 队列模式(竞争消费)。多消费者组 → 发布订阅模式(广播消费)。

消费者组工作机制

  1. 分区分配:消费者加入组时,由 Coordinator 分配 Partition(策略包括 Range、RoundRobin、Sticky)。
  2. 负载均衡:组内消费者数量与 Partition 数量匹配时,每个消费者处理固定 Partition。
  3. 再平衡(Rebalance):消费者增减或故障时,重新分配 Partition,期间服务短暂不可用。

Kafka 的 offset 管理是怎样的?为什么同一个消费者组的消费者不能消费相同的分区?

Offset 管理机制

  • 存储位置: 旧版本依赖 Zookeeper,新版本使用内部 Topic __consumer_offsets,按 Group ID 和 Partition 存储 Offset。
  • 提交方式: 自动提交:定期提交(可能重复或丢失数据)。手动提交:业务处理完成后调用 commitSync() 或 commitAsync()。

同组消费者禁止消费相同分区

  • 设计目标:避免重复消费,保证消息处理的并行性和顺序性。
  • 分配原则:每个 Partition 只能被组内一个消费者独占消费。
  • 例外场景:若消费者数量超过 Partition 数,多余消费者将处于闲置状态。

如果有一条 offset 对应的数据,消费完成之后,手动提交失败,如何处理?正在消费一条数据时 Kafka 挂了,重启以后,消费的 offset 是哪一个?

手动提交失败处理

  1. 重试提交:在代码中捕获提交异常,加入重试逻辑(如指数退避)。
  2. 业务补偿:若消息处理是幂等的,可允许重复消费。
  3. 记录状态:将 Offset 与业务结果一起存储到数据库,通过事务保证一致性。

Kafka 重启后的 Offset 恢复

  • 提交策略决定 Offset: 若手动提交失败,消费者重启后会从最后一次成功提交的 Offset 开始消费,导致已处理但未提交的消息被重复消费。若使用自动提交,可能因提交周期未到而丢失部分 Offset。

Kafka 支持什么语义,怎么实现 Exactly Once?消费者和消费者组有什么区别,为什么需要消费者组?

消息语义

  • At Most Once:消息可能丢失,但不会重复(ACK=0)。
  • At Least Once:消息可能重复,但不会丢失(ACK=1 或 all,默认模式)。
  • Exactly Once:消息仅处理一次,需结合以下机制: 生产者幂等性:通过唯一 PID 和序列号去重。事务机制:跨生产者和消费者的原子操作(isolation.level=read_committed)。

消费者 vs 消费者组

  • 消费者:单个进程或线程,负责从指定 Partition 拉取数据。
  • 消费者组:多个消费者组成的逻辑单元,实现: 并行消费:组内消费者共同处理一个 Topic 的多个 Partition。负载均衡:动态分配 Partition 以应对消费者增减。容错性:某个消费者故障时,其处理的 Partition 会被重新分配。

需要消费者组的原因

  • 横向扩展消费能力,适应高吞吐场景。
  • 支持多种消费模式(队列或发布订阅)。

Kafka producer 的写入数据过程是怎样的?ack 设置有哪些,ack 机制解决了什么问题?

Producer 写入流程可以拆解为几个关键步骤:

  1. 数据序列化:将消息 Key 和 Value 按配置的序列化方式(如 Avro、JSON)转换为字节流。
  2. 选择分区:根据分区策略(轮询、哈希、自定义)确定目标 Partition。
  3. 批次聚合:消息不会立即发送,而是按 linger.ms(等待时间)和 batch.size(批次大小)参数累积成批次,提升吞吐量。
  4. 发送至 Broker:批次数据通过网络发送到对应 Partition 的 Leader Broker。
  5. ACK 确认:Broker 根据 ACK 配置返回确认信号,生产者决定是否重试。

ACK 参数类型

0

不等待确认,直接发送下一条消息。

高吞吐但容忍数据丢失(如日志采集)。

1

Leader 写入本地日志即返回确认。

平衡可靠性与性能(默认配置)。

all(-1)

所有 ISR 副本写入成功后才确认。

要求高可靠性(如金融交易)。

ACK 机制解决的问题

  • 数据丢失风险:通过副本写入确认降低消息丢失概率。
  • 一致性保障:控制数据在集群中的副本同步级别。

Kafka 读取消息是推还是拉的模式,有什么好处?如何实现高吞吐?

Kafka 采用 消费者主动拉取(Pull) 模式,而非服务端推送(Push)。

拉模式的优势

  • 消费速率可控:消费者根据自身处理能力拉取数据,避免被压垮。
  • 批量处理:通过 max.poll.records 配置单次拉取消息数量,提升处理效率。
  • 减少无效推送:消费者离线时,服务端无需维护推送状态。

实现高吞吐的关键手段

  1. 分区并行:多个消费者同时读取不同 Partition,横向扩展消费能力。
  2. 零拷贝技术:使用 sendfile 系统调用,减少内核态与用户态数据拷贝。
  3. 批量压缩:生产者端对批次数据压缩(如 Snappy、GZIP),降低网络传输量。
  4. 高效存储:消息按顺序追加到磁盘,利用页缓存加速读写。

说下 Kafka 中的 Partition?数据是如何备份的?存的数据格式是什么样的?

Partition 核心概念

  • 物理分片:每个 Topic 被划分为多个 Partition,分布在不同 Broker。
  • 有序性保证:Partition 内部消息严格按写入顺序存储,支持顺序读写。
  • 扩展性基础:通过增加 Partition 数量提升 Topic 的吞吐能力。

数据备份机制

  1. 副本(Replica):每个 Partition 配置多个副本(如 replication factor=3)。
  2. Leader-Follower 模型: Leader:处理所有读写请求。Follower:从 Leader 异步或同步拉取数据,保持数据同步。
  3. ISR 列表:仅处于同步状态的副本可被选为 Leader。

数据存储格式

  • 日志分段:Partition 对应一个目录,数据按 segment.bytes 切分为多个日志文件(如 0000000000.log)。
  • 索引文件:每个日志段附带 .index(偏移量索引)和 .timeindex(时间戳索引)文件,加速消息查找。
  • 消息结构: 固定头部:包含 CRC 校验、版本号、时间戳等元数据。可变长度部分:Key 和 Value 的二进制内容。

Kafka 是如何清理过期文件的?一条 message 中包含了哪些信息?

数据清理策略

  1. 基于时间:通过 log.retention.hours 配置保留时长(默认 7 天),超时文件删除。
  2. 基于大小:通过 log.retention.bytes 限制 Topic 总大小,超出部分删除旧数据。
  3. 压缩清理(Compact):针对 Key 保留最新值,适用于状态更新类数据(如用户配置变更)。

Message 结构

Offset

消息在 Partition 内的唯一序号。

Key

可选字段,用于分区路由或业务标识。

Value

消息内容,通常为业务数据。

Timestamp

消息生成时间或写入时间。

Headers

键值对形式的元数据(如跟踪 ID)。

如何保证 Kafka 数据的 Exactly Once,消费者怎么保证 Exactly Once?Kafka 监控如何实现?

Exactly Once 实现

  • 生产者端: 幂等性:通过唯一 PID(Producer ID)和序列号(Sequence Number)去重。事务机制:跨多个 Partition 的原子写入,需配合 transactional.id 使用。
  • 消费者端: 事务型消费:读取消息后,将 Offset 提交与业务处理绑定到同一事务。幂等处理:业务逻辑设计为可重复执行(如数据库唯一约束)。

监控实现方式

  1. JMX 指标:Kafka 暴露数百个 JMX 指标(如消息吞吐量、副本延迟)。
  2. Exporter 工具:使用 Prometheus + JMX Exporter 或 Kafka Exporter 收集指标。
  3. 集群管理工具:Confluent Control Center、Kafka Manager 提供可视化监控。
  4. 自定义监控:通过 Consumer Lag API 监控消费延迟,或解析日志分析异常。

Kafka 中的数据能彻底删除吗?复制机制、分区多副本机制、分区分配算法是怎样的?

数据删除机制

Kafka 的数据删除并非立即物理删除,而是基于 日志保留策略

  • 时间策略:通过 log.retention.hours 配置保留时长(如 7 天),超时数据标记为可删除。
  • 空间策略:通过 log.retention.bytes 限制 Topic 总大小,超出时删除旧数据。
  • 手动删除:使用 kafka-delete-records 工具指定 Offset 删除,但实际删除由后台线程异步执行。

彻底删除的挑战

  • 数据可能仍存在于副本或备份中。
  • 若配置了日志压缩(Compact),只会保留相同 Key 的最新值,而非物理删除旧数据。

复制与多副本机制

副本(Replica)

每个 Partition 的多个副本分布在不同 Broker,分为 Leader 和 Follower。

Leader 职责

处理所有读写请求,Follower 仅同步数据。

ISR 列表

仅同步的副本(In-Sync Replicas)可参与 Leader 选举。

数据同步流程

  1. 生产者发送消息到 Leader。
  2. Leader 将消息写入本地日志。
  3. Follower 从 Leader 拉取消息并写入本地。
  4. Leader 确认所有 ISR 副本写入成功后返回 ACK。

分区分配算法

生产者分区策略

  • 轮询策略:均匀分布消息,适合无 Key 的场景。
  • 哈希策略:按 Key 的哈希值分配,保证相同 Key 的消息进入同一分区。
  • 自定义策略:根据业务逻辑(如地理位置)分配。

消费者分区分配策略

Range

按分区范围分配给消费者(可能负载不均)。

分区数固定的场景。

RoundRobin

轮询分配,实现均衡。

消费者数量动态变化。

Sticky

尽量保留原有分配,减少再平衡时的数据迁移。

高频再平衡场景。

Kafka 蓄水池机制是什么?如何实现幂等性?offset 存在哪?如何保证数据一致性?

蓄水池机制

生产者缓冲池,用于累积消息并批量发送,核心参数:

  • batch.size:批次大小阈值(如 16KB),达到后立即发送。
  • linger.ms:等待时间(如 5ms),超时后发送当前批次。作用:减少网络请求次数,提升吞吐量,但可能增加延迟。

幂等性实现

  • 生产者端: 启用 enable.idempotence=true,通过 唯一 PID(Producer ID)序列号(Sequence Number) 去重。
  • 消费者端: 业务逻辑设计为幂等操作(如数据库唯一索引)。

Offset 存储位置

  • 旧版本:依赖 Zookeeper,性能受限。
  • 新版本:使用内部 Topic __consumer_offsets,按 Group ID 和 Partition 存储 Offset,支持高并发读写。

数据一致性保障

  • 生产者 ACK:设置 acks=all 确保消息写入所有 ISR 副本。
  • 副本同步:Leader 定期检查 Follower 同步状态,滞后副本移出 ISR。
  • 消费者提交:手动提交 Offset,确保消息处理完成后才更新消费位置。

Kafka 新旧 API 区别有哪些?消息在磁盘上的组织方式是怎样的?

新旧 API 对比

线程安全

非线程安全

线程安全

异步支持

有限

支持

send()

异步回调

配置灵活性

参数较少

细粒度控制(如拦截器、序列化器)

维护状态

已逐步弃用

官方推荐

磁盘数据组织

  • 分区目录结构: Topic 名称 + 分区号(如 topic-0),包含多个日志段文件。
  • 日志段(Segment): 文件名基于基准 Offset(如 00000000000012345678.log)。大小由 segment.bytes 控制(默认 1GB)。
  • 索引文件: .index:Offset 到物理位置的映射。.timeindex:时间戳到 Offset 的映射。

消息追加流程

  1. 新消息追加到当前活跃 Segment。
  2. 当 Segment 达到大小或时间阈值,滚动生成新文件。
  3. 索引文件实时更新以加速查询。

Kafka 在哪些地方会有选举过程,使用什么工具支持选举?搭建过程要配置什么参数?单播和多播是怎样的?

选举场景

  1. Controller 选举: 旧版本通过 Zookeeper 选举,新版本(KRaft 模式)使用 Raft 协议。Controller 负责 Partition 状态管理和副本选举。
  2. Partition Leader 选举: 当 Leader 故障时,从 ISR 列表中选择新 Leader。

选举工具

  • Zookeeper:旧版本的核心依赖,管理集群元数据。
  • KRaft:新版本内置的 Raft 实现,逐步替代 Zookeeper。

搭建关键参数

broker.id

Broker 唯一标识

0

zookeeper.connect

Zookeeper 地址(旧版)

zk1:2181,zk2:2181

listeners

监听地址

PLAINTEXT://:9092

log.dirs

数据存储目录

/data/kafka-logs

num.partitions

默认分区数

3

default.replication.factor

默认副本数

3

单播与多播

  • 单播(Unicast): 点对点通信,如生产者将消息发送到特定 Broker 的 Leader Partition。
  • 多播(Multicast): 一对多通信,如消费者组订阅 Topic 时,每个消费者组独立接收全量数据。

配置关联

  • advertised.listeners 定义 Broker 对外暴露的地址,影响客户端连接方式。
  • 跨网络环境时需配置正确协议(如 PLAINTEXT、SSL)。

Kafka 的高水位和 Leader Epoch 是什么?分区器、拦截器、序列化器的作用是什么?

高水位(High Watermark)

  • 定义:消费者可见的最大 Offset,表示已成功复制到所有 ISR 副本的消息位置。
  • 作用:防止消费者读取未完全同步的数据(如 Leader 已写入但 Follower 未同步的消息)。

Leader Epoch

  • 背景:旧版本 Leader 切换时可能因日志截断导致数据不一致。
  • 机制: 每个 Leader 任期分配唯一 Epoch 编号,写入消息时附带 Epoch,用于检测副本间数据冲突。
  • 解决的问题: 避免因网络分区或宕机导致的消息丢失或重复。

核心组件作用

分区器(Partitioner)

决定消息发送到哪个分区

哈希策略、轮询策略

拦截器(Interceptor)

在消息发送前后插入自定义逻辑

日志记录、消息修改

序列化器(Serializer)

将对象转换为字节流

StringSerializer

AvroSerializer

典型场景

  • 分区器:按用户 ID 哈希,确保同一用户操作进入同一分区。
  • 拦截器:统计发送成功率或添加链路追踪信息。
  • 序列化器:使用 Avro 压缩数据并兼容 Schema 变更。

Kafka 连接 Spark Streaming 的几种方式有哪些?生成者客户端有几个线程?怎么防止脑裂?高可用体现在哪里?

连接 Spark Streaming 的方式

Spark Streaming 与 Kafka 集成主要有两种模式:

  • Receiver-based(已弃用): 通过 Kafka Receiver 在 Executor 上创建线程持续拉取数据,存在 数据丢失风险(Receiver 故障时可能丢失 WAL 未存储的数据)。缺点:需要预分配资源,吞吐量受限于 Receiver 数量。
  • Direct(Direct Stream): 直接通过 Spark 任务从 Kafka Partition 拉取数据,利用 Offset 范围控制,实现精确一次语义。优点:无需 Receiver,资源动态分配,支持故障恢复。
  • Structured Streaming: 基于 DataFrame API 的流处理,支持持续查询和端到端 Exactly-Once。

生产者客户端线程

生产者客户端包含两类线程:

  1. 主线程:负责消息序列化、分区选择、批次聚合。
  2. Sender 线程:后台线程,将批次数据发送到 Broker,处理 ACK 响应和重试。

线程隔离设计:主线程与 Sender 线程分离,避免阻塞业务逻辑。

防止脑裂(Split-Brain)

脑裂指集群中多个 Broker 误认为自己是 Leader,导致数据不一致。Kafka 通过以下机制避免:

  • Controller 唯一性:集群中仅一个 Controller 负责 Partition 状态管理,通过 Zookeeper 或 KRaft 协议选举。
  • Epoch 机制:每个 Leader 任期分配唯一 Epoch,旧 Leader 的写入请求会被拒绝。
  • Zookeeper 协调(旧版本):通过临时节点监控 Broker 活性,故障时触发重新选举。

高可用性体现

  1. 多副本机制:每个 Partition 有多个副本,Leader 故障时自动切换至 ISR 副本。
  2. 自动故障转移:Controller 监控 Broker 状态,触发 Leader 重新选举。
  3. 消费者无缝切换:消费者组自动感知 Partition Leader 变化,继续从新 Leader 拉取数据。

Zookeeper 在 Kafka 中有什么作用?

Zookeeper 在 旧版本 Kafka(2.8 之前) 中扮演核心角色,负责:

  1. Broker 注册:维护 Broker 列表及元数据(如监听地址)。
  2. Controller 选举:通过临时节点选举唯一 Controller,负责 Partition 状态管理。
  3. Topic 配置管理:存储 Topic 的分区数、副本分配等元数据。
  4. 消费者 Offset 存储(旧版本):记录消费者组的消费进度。

新版本 Kafka(KRaft 模式) 逐步移除 Zookeeper 依赖,改由 内置 Raft 协议 实现元数据管理,提升稳定性和性能。

Kafka 是否碰到过阻塞情况?如果碰到了是如何处理的?

常见阻塞场景

  1. 生产者阻塞: 原因:Broker 处理能力不足或网络延迟,导致 ACK 响应超时。解决:优化 max.block.ms(生产者缓冲区阻塞超时时间),增加 Broker 资源或分区数。
  2. 消费者阻塞: 原因:消息处理逻辑耗时过长,导致 max.poll.interval.ms 超时,触发 Rebalance。解决:异步处理消息或提升消费者并发度。
  3. 磁盘 I/O 瓶颈: 原因:日志写入速度跟不上生产者速率,导致 Broker 响应延迟。解决:使用 SSD 硬盘,调整 num.io.threads(Broker I/O 线程数)。

处理策略

  • 监控告警:通过 JMX 监控 Producer/Consumer Lag、Broker 负载等指标。
  • 动态扩容:增加 Partition 和 Broker,分散写入压力。
  • 优化配置:调整 batch.size(批次大小)、linger.ms(等待时间)提升吞吐。

请介绍 Kafka 的 ISR、OSR 和 ACK,ACK 分别有几种值?分区分配算法是怎样的?蓄水池机制是什么?

ISR 与 OSR

  • ISR(In-Sync Replicas):与 Leader 数据同步的副本集合,参与 Leader 选举。
  • OSR(Out-of-Sync Replicas):滞后于 Leader 的副本,不参与选举。
  • 同步条件:Follower 与 Leader 的 消息差值时间差 不超过阈值(replica.lag.time.max.ms)。

ACK 参数

0

不等待确认

最低(可能丢失数据)。

1

Leader 写入成功

中等(Leader 故障可能丢失数据)。

all

所有 ISR 副本写入成功

最高(需配置

min.insync.replicas

)。

分区分配算法

Range

按分区范围分配,可能导致负载不均。

分区数固定且消费者数较少。

RoundRobin

轮询分配,均衡但可能破坏顺序。

动态消费者数量。

Sticky

尽量保留原有分配,减少 Rebalance 数据迁移。

高频扩缩容场景。

蓄水池机制

生产者批次缓冲池,通过参数控制消息累积:

  • batch.size:批次大小阈值(如 16KB),达到后立即发送。
  • linger.ms:等待时间(如 5ms),超时后发送当前批次。作用:减少网络请求次数,提升吞吐量,但可能增加延迟。

Kafka 在传输数据过程中如果发生断电,如何保证数据的可靠性?

生产者端保障

  1. ACK=all:确保消息写入所有 ISR 副本后才返回确认。
  2. 重试机制:设置 retries=Integer.MAX_VALUE,在 Broker 恢复后重试发送。
  3. 幂等性:启用 enable.idempotence=true,避免重复消息。

Broker 端保障

  1. 副本同步:每个 Partition 有多个副本,Leader 故障时自动切换至 ISR 副本。
  2. 刷盘策略: 异步刷盘:通过 flush.messages 和 flush.ms 控制刷盘频率,平衡性能与可靠性。强制刷盘:极端场景下使用 unclean.leader.election.enable=false 禁止非 ISR 副本成为 Leader。

消费者端保障

  1. 手动提交 Offset:确保消息处理完成后才提交 Offset。
  2. 事务消费:结合 Kafka 事务,将消息处理与 Offset 提交绑定到同一事务。

总结:通过 多副本持久化生产者重试消费者精确提交 三重机制,最大程度降低断电导致的数据丢失风险。

如何给 Kafka 消息添加前缀?在分布式场景下,全局有序 id 如何生成?具体到每一个细节,如何在多台机器上获取这些分布式 id?

消息添加前缀的实现

生产者端处理:在消息发送前对 Key 或 Value 进行字符串拼接。例如:

String originalValue = "data_content";
String prefixedValue = "PREFIX_" + originalValue;
ProducerRecord<String, String> record = new ProducerRecord<>("topic", prefixedValue);

拦截器(Interceptor):通过自定义 ProducerInterceptor 在消息发送前统一添加前缀,避免业务代码侵入。

全局有序 ID 生成方案

Snowflake 算法

  • 结构:64 位 ID = 时间戳(41 位) + 机器 ID(10 位) + 序列号(12 位)。
  • 细节: 时间戳:从自定义纪元(如 2020-01-01)开始的毫秒数,支持约 69 年。机器 ID:通过 ZooKeeper 或配置文件分配,确保集群内唯一。序列号:同一毫秒内的自增数(0~4095),超出时等待下一毫秒。
  • 防时钟回拨:使用 NTP 同步时钟,或记录上次时间戳,检测到回拨时抛出异常。

数据库分段方案

  1. 创建表 id_segment,字段包括业务类型、当前最大值、步长(如 1000)。
  2. 每次服务启动时从数据库申请一个区间(如当前值=5000,步长=1000,则分配 5001~6000)。
  3. 内存中自增使用,用完后重新申请。

Redis 原子操作

INCR global_id  # 返回自增整数,结合业务前缀生成有序 ID  

缺点:Redis 单点故障可能导致 ID 不连续。

多机分布式 ID 获取细节

  1. 机器 ID 分配: 静态配置:每台机器启动时读取配置文件中的唯一 ID(如 machine_id=1)。动态注册:通过 ZooKeeper 临时节点生成唯一 ID,机器下线后自动释放。
  2. 协调服务: ZooKeeper:创建 /id_generators 节点,子节点为顺序节点(如 generator_0001)。Etcd:利用其分布式锁和原子操作分配 ID 区间。
  3. 容错处理: 定期将本地 ID 状态持久化到磁盘,故障恢复后从断点继续。

如何知道 Spark Streaming 当前消费到了 Kafka 的哪些分区,以及消费到的 offset 到哪了?

监控消费状态的常用方法

通过 Checkpoint 获取

  1. 启用 Spark Streaming 的 Checkpoint 机制,定期将消费 Offset 写入 HDFS。
  2. 解析 Checkpoint 文件中的 offsets 数据,获取各 Partition 的当前 Offset。

外部存储记录

  • Redis/HBase:在每批次处理完成后,将 Topic、Partition、Offset 写入外部存储。
dstream.foreachRDD { rdd =>
  val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
  offsetRanges.foreach { range =>
    writeToRedis(range.topic, range.partition, range.untilOffset)
  }
}

Kafka Consumer API 查询

使用 KafkaConsumer#committed() 方法获取消费者组提交的 Offset:

Consumer consumer = new KafkaConsumer<>(props);
Map<TopicPartition, OffsetAndMetadata> offsets = consumer.committed(new HashSet<>(partitions));

可视化工具

  • Kafka Manager:查看消费者组的 Lag(未消费消息数)。
  • Prometheus + Grafana:通过 kafka_consumergroup_lag 指标监控实时消费进度。

Kafka 事件数据重复的问题如何解决?

生产者端去重

  1. 幂等性(Idempotence): 启用 enable.idempotence=true,生产者自动添加 PID 和序列号,Broker 端去重。限制:仅保证单分区、单会话的幂等性。
  2. 事务(Transactions): 跨分区原子写入,结合 transactional.id 实现跨会话幂等。

消费者端去重

  1. 幂等写入外部存储: 数据库唯一键:通过 INSERT IGNORE 或 ON CONFLICT DO NOTHING 避免重复。Redis Set:存储已处理消息的 ID,SADD 操作天然去重。
  2. Offset 与业务结果原子提交: 将 Offset 和业务结果写入同一数据库事务,确保处理完成才提交 Offset。
  3. 消息指纹(如 SHA-256): 计算消息内容的哈希值,存储在去重表中,消费前校验是否存在。

流处理框架层去重

  • Flink 状态去重:使用 KeyedState 记录已处理的消息 ID。
  • Spark Streaming 窗口去重:在滑动窗口内根据业务 ID 去重。

使用 flink-client 消费 kafka 数据还是使用 flink-connector 消费?

Flink-Connector 的核心优势

  1. 内置集成: 官方维护的 flink-connector-kafka 支持 Exactly-Once 语义、动态分区发现等功能。自动管理 Offset,并与 Flink 检查点机制深度集成。
  2. 简化开发: 通过预定义的 FlinkKafkaConsumer 和 FlinkKafkaProducer 直接连接 Kafka。
  3. 精准恢复: Checkpoint 时保存 Offset,故障恢复后从保存点继续消费,避免数据丢失或重复。

Flink-Client 的适用场景

  • 自定义消费逻辑:需直接操作 Kafka Consumer API 实现特殊需求(如多级过滤)。
  • 低级别控制:手动管理 Offset 提交或绕过 Flink 的状态机制(极少使用)。

结论优先使用 flink-connector,除非有极端定制化需求。

17年+码农经历了很多次面试,多次作为面试官面试别人,多次大数据面试和面试别人,深知哪些面试题是会被经常问到。 在多家企业从0到1开发过离线数仓实时数仓等多个大型项目,详细介绍项目架构等企业内部秘不外传的资料,介绍踩过的坑和开发干货,分享多个拿来即用的大数据ETL工具,让小白用户快速入门并精通,指导如何入职后快速上手。 计划更新内容100篇以上,包括一些企业内部秘不外宣的干货,欢迎订阅!

全部评论

相关推荐

评论
3
16
分享

创作者周榜

更多
牛客网
牛客企业服务