Kafka知识点

Kafka 基础概念详解

Kafka 是什么?有什么作用?

Apache Kafka 是一个开源的分布式事件流平台,最初由 LinkedIn 开发并开源。它基于发布 - 订阅模式,设计目标是处理高吞吐量、实时数据流。Kafka 的核心价值在于:

  1. 消息系统

    • 实现系统间松耦合通信
    • 支持多生产者和多消费者
    • 提供消息持久化存储
  2. 数据管道

    • 高效收集和传输大量数据
    • 支持实时数据流处理
    • 作为微服务架构中的事件总线
  3. 数据存储

    • 基于磁盘的持久化存储
    • 可配置的数据保留策略
    • 支持数据重放和回溯消费
  4. 流处理平台

    • 内置 Kafka Streams API
    • 与 Flink、Spark 等流处理框架集成
    • 支持实时数据转换和分析
Kafka 的架构详解

Kafka 架构主要由以下组件构成:

  1. Producer(生产者)

    • 负责创建和发送消息到 Kafka 集群
    • 支持同步和异步发送模式
    • 可以自定义分区策略
    • 实现批量发送以提高吞吐量
  2. Consumer(消费者)

    • 从 Kafka 集群读取消息
    • 维护消费偏移量(offset)
    • 支持水平扩展(通过消费者组)
    • 可以手动控制消费位置
  3. Consumer Group(消费者组)

    • 一组协同工作的消费者实例
    • 实现负载均衡消费
    • 确保每个分区只被组内一个消费者消费
    • 支持故障自动转移
  4. Broker(代理)

    • Kafka 集群中的单个服务器节点
    • 存储消息数据
    • 处理读写请求
    • 维护分区的副本
  5. Topic(主题)

    • 消息的逻辑分类
    • 类似于数据库中的表
    • 每个主题可以有多个分区
    • 支持多租户隔离
  6. Partition(分区)

    • 主题的物理分片
    • 消息在分区内有序存储
    • 每个分区可以有多个副本
    • 提高并发处理能力
  7. Replica(副本)

    • 分区的冗余备份
    • 包括一个 Leader 和多个 Follower
    • Leader 处理所有读写请求
    • Follower 被动复制 Leader 数据
  8. Controller

    • 特殊的 Broker 节点
    • 负责集群管理
    • 处理分区 Leader 选举
    • 监控 Broker 状态
  9. Zookeeper(2.8.0 之前)

    • 存储集群元数据
    • 协调 Broker 之间的工作
    • 记录分区和副本分配
    • 监控 Broker 状态
Zookeeper 对于 Kafka 的作用

在 Kafka 2.8.0 版本之前,Zookeeper 扮演了关键角色:

  1. 元数据管理

    • 存储 Broker 注册信息
    • 维护 Topic 和分区信息
    • 记录副本分配情况
  2. 集群协调

    • 选举 Controller 节点
    • 处理 Broker 加入和退出
    • 管理分区重分配
  3. 配置管理

    • 存储动态配置参数
    • 支持配置变更通知
  4. 消费者协调

    • 管理消费者组
    • 存储消费者偏移量(旧版)
    • 协调消费者分区分配

在 2.8.0 版本后,Kafka 引入了 KRaft 模式(Kafka Raft Metadata),不再依赖 Zookeeper:

  • 使用 Raft 协议管理元数据
  • 减少系统复杂度
  • 提高集群稳定性
  • 简化部署和运维

生产者相关详解

生产者的发送消息的分区策略

Kafka 提供了多种分区策略:

  1. 轮询策略(Round Robin)

    • 默认分区策略
    • 按顺序将消息发送到各个分区
    • 确保消息均匀分布
    • 适合不需要特定顺序的场景
  2. 随机策略(Random)

    • 随机选择分区
    • 实现简单但分布可能不均匀
    • 实际使用较少
  3. 按 key 哈希策略(Keyed)

    • 根据消息的 key 计算哈希值
    • 相同 key 的消息总是发送到同一分区
    • 保证相关消息的顺序性
    • 适合需要保持特定顺序的场景
  4. 粘性分区策略(Sticky Partitioning)

    • 2.4 版本引入的优化策略
    • 临时固定使用一个分区,攒够一批消息后再换
    • 减少元数据请求次数
    • 提高吞吐量
  5. 自定义分区策略

    • 实现 Partitioner 接口
    • 根据业务需求定制分区逻辑
    • 例如根据消息内容或业务规则选择分区
Kafka 的 ack 的三种机制详解

生产者通过 acks 参数控制消息确认机制:

  1. acks=0

    • 生产者发送消息后立即返回
    • 不等待任何服务器确认
    • 吞吐量最高,但可靠性最低
    • 可能丢失消息(如网络故障)
  2. acks=1

    • 生产者等待 Leader 副本接收消息
    • Leader 写入本地日志后返回确认
    • 部分可靠性保障
    • 如果 Leader 崩溃且未复制到 Follower,消息可能丢失
  3. acks=all(或 -1)

    • 生产者等待所有 ISR 副本确认
    • 只有当所有 ISR 副本都接收到消息才返回
    • 最高可靠性保障
    • 吞吐量最低,延迟最高
    • 可配置 min.insync.replicas 确保至少有指定数量的副本同步
生产者如何保证消息的可靠性

要确保消息不丢失,需要综合配置以下参数:

  1. acks=all

    • 确保所有 ISR 副本都收到消息
  2. retries > 1

    • 发送失败时自动重试
    • 可配置重试间隔和最大重试次数
  3. retry.backoff.ms

    • 重试间隔,避免频繁重试导致网络拥塞
  4. delivery.timeout.ms

    • 消息发送超时时间
  5. max.in.flight.requests.per.connection > 1

    • 控制并发请求数
    • 设置为 1 可保证消息顺序性
  6. enable.idempotence=true

    • 启用幂等性生产者
    • 自动处理重试导致的重复消息
    • 仅在同一个会话内有效
  7. transactional.id

    • 配置事务 ID
    • 实现跨分区、跨主题的原子性操作
    • 确保消息的 "恰好一次" 语义
  8. 错误处理机制

    • 捕获发送异常
    • 记录失败消息
    • 实现补偿机制

消费者相关详解

consumer 是推还是拉?有什么优劣势?

Kafka 采用 pull(拉)模式从 Broker 获取数据:

优势

  • 消费者控制消费速率,避免被快速生产者压垮
  • 支持批量消费,提高吞吐量
  • 易于实现水平扩展
  • 消费者可以根据需要回溯消费历史数据

劣势

  • 如果没有数据,消费者会空轮询,浪费资源
  • 可能导致消息处理延迟(需合理配置参数)

优化措施

  • fetch.min.bytes:设置最小拉取数据量,避免频繁空请求
  • fetch.max.wait.ms:设置最大等待时间,避免长时间阻塞
  • max.poll.records:控制一次拉取的最大消息数
  • enable.auto.commit:控制是否自动提交偏移量
消费者如何不自动提交偏移量,由应用提交?

手动提交偏移量的步骤:

  1. 配置消费者

    enable.auto.commit=false
    
  2. 处理消息后手动提交

    // 同步提交
    consumer.commitSync();
    
    // 异步提交
    consumer.commitAsync();
    
    // 带回调的异步提交
    consumer.commitAsync((offsets, exception) -> {
        if (exception != null) {
            // 处理提交失败的情况
        }
    });
    
    // 提交特定偏移量
    Map offsets = new HashMap<>();
    offsets.put(new TopicPartition(topic, partition), new OffsetAndMetadata(nextOffset));
    consumer.commitSync(offsets);
    
  3. 提交时机选择

    • 处理完一批消息后提交
    • 处理完特定业务逻辑后提交
    • 定时提交
消费者故障,出现活锁问题如何解决?

活锁是指消费者不断重新平衡但无法处理消息的状态,通常由以下原因导致:

  1. 处理时间过长

    • 消息处理逻辑复杂
    • 外部资源依赖(如数据库、API)响应慢
  2. 配置不合理

    • max.poll.records 设置过大
    • session.timeout.ms 设置过小
    • max.poll.interval.ms 设置过小

解决方案

  • 优化消息处理逻辑,减少处理时间
  • 增加消费者实例,分担负载
  • 调整配置参数:
    • 增大 max.poll.interval.ms
    • 减小 max.poll.records
    • 增大 session.timeout.ms
  • 使用异步处理机制,将耗时操作放入线程池
  • 实现消息批处理,减少处理频率
如何控制消费的位置?

消费者可以通过以下方式控制消费位置:

  1. 从特定偏移量开始消费

    // 从指定偏移量开始消费
    consumer.seek(topicPartition, offset);
    
    // 从最早的消息开始消费
    consumer.seekToBeginning(Collections.singleton(topicPartition));
    
    // 从最新的消息开始消费
    consumer.seekToEnd(Collections.singleton(topicPartition));
    
  2. 根据时间戳定位

    // 构建时间戳到分区的映射
    Map timestampsToSearch = new HashMap<>();
    timestampsToSearch.put(topicPartition, targetTimestamp);
    
    // 获取符合时间戳的偏移量
    Map offsetsForTimes = consumer.offsetsForTimes(timestampsToSearch);
    
    // 定位到该偏移量
    OffsetAndTimestamp offsetAndTimestamp = offsetsForTimes.get(topicPartition);
    if (offsetAndTimestamp != null) {
        consumer.seek(topicPartition, offsetAndTimestamp.offset());
    }
    
  3. 消费者组重置策略

    # 当没有初始偏移量或当前偏移量无效时的策略
    auto.offset.reset=earliest  # 从最早的消息开始
    auto.offset.reset=latest    # 从最新的消息开始
    auto.offset.reset=none      # 抛出异常
    

分区与副本相关详解

Kafka Partition 副本 leader 是怎么选举的?

当 Leader 副本所在 Broker 发生故障时,会触发 Leader 选举:

  1. Controller 检测故障

    • Controller 监控所有 Broker 状态
    • 当发现 Leader 所在 Broker 不可用时,触发选举流程
  2. 从 ISR 中选择新 Leader

    • 优先选择 ISR 集合中的副本
    • 通常选择 LEO(Log End Offset)最大的副本
    • 可以通过 unclean.leader.election.enable 参数控制是否允许非 ISR 副本成为 Leader
  3. 更新元数据

    • Controller 更新 Zookeeper/KRaft 中的分区 Leader 信息
    • 通知所有相关 Broker 新的 Leader 分配
  4. 通知消费者

    • 消费者通过元数据请求获取新的 Leader 信息
    • 重新连接到新的 Leader
分区数越多越好吗?吞吐量就会越高吗?

分区数与吞吐量的关系较为复杂:

正面影响

  • 增加并行度,允许多个消费者同时处理
  • 提高磁盘 I/O 利用率
  • 支持更高的写入吞吐量

负面影响

  • 增加 Broker 管理开销
  • 增加客户端连接数
  • 增加内存使用(每个分区需要维护状态)
  • 增加 Leader 选举时间
  • 过多分区可能导致数据倾斜
  • 降低单分区的吞吐量(由于资源竞争)

合理设置分区数

  • 考虑生产者写入能力
  • 考虑消费者处理能力
  • 考虑集群 Broker 数量
  • 考虑磁盘 I/O 能力
  • 通常建议每个 Broker 管理 1000-2000 个分区
  • 初始设置后可通过分区重分配调整
Kafka 如何保证分布式情况下消息的顺序消费?

Kafka 保证消息顺序性的机制:

  1. 单分区内的顺序性

    • Kafka 保证消息在单个分区内严格有序
    • 生产者发送到同一分区的消息按发送顺序存储
  2. 跨分区的顺序性

    • 通过将相关消息发送到同一分区保证
    • 使用相同 key 的消息会被路由到同一分区
    • 消费者组中只有一个消费者处理该分区
  3. 实现方式

    • 生产者使用按 key 哈希分区策略
    • 消费者组中消费者数量不超过分区数
    • 每个分区只由一个消费者处理
    • 可通过增加分区数提高并发度,但仍受限于分区数量
  4. 性能权衡

    • 严格顺序消费会降低系统吞吐量
    • 分布式系统中强顺序性与高可用性难以兼得
    • 实际应用中需根据业务需求平衡顺序性和性能

其他重要机制详解

Kafka 的高可用机制是什么?

Kafka 通过多副本机制实现高可用性:

  1. 副本分配

    • 每个分区有多个副本(通常 2-3 个)
    • 副本分布在不同 Broker 上
    • 通过 replication.factor 参数控制副本数
  2. ISR 机制

    • In-Sync Replicas 集合
    • 包含所有与 Leader 保持同步的副本
    • 通过 replica.lag.time.max.ms 参数定义同步标准
    • 只有 ISR 中的副本才有资格成为 Leader
  3. Leader 选举

    • Controller 负责 Leader 选举
    • 从 ISR 中选择新 Leader
    • 确保数据一致性和可用性
  4. 数据同步

    • Follower 定期从 Leader 拉取数据
    • 使用 HW(High Watermark)和 LEO 机制保证数据一致性
    • 只有被所有 ISR 副本确认的消息才对消费者可见
  5. Broker 故障恢复

    • Controller 检测到 Broker 故障
    • 触发相关分区的 Leader 选举
    • 新 Leader 继续提供服务
    • 故障 Broker 恢复后作为 Follower 重新加入
Kafka 如何减少数据丢失?

从多个层面保证数据不丢失:

  1. 生产者层面

    • acks=all:确保所有 ISR 副本都收到消息
    • 合理设置 retries 和 retry.backoff.ms
    • 启用幂等性和事务
    • min.insync.replicas > 1:确保至少有指定数量的副本同步
  2. Broker 层面

    • 合理设置 unclean.leader.election.enable=false:禁止非 ISR 副本成为 Leader
    • 配置合理的刷盘策略:
      • log.flush.interval.messages:多少条消息刷一次盘
      • log.flush.interval.ms:多长时间刷一次盘
    • 监控 ISR 状态,及时处理异常
  3. 消费者层面

    • 手动提交偏移量
    • 先处理消息再提交偏移量
    • 处理消息时实现幂等性
    • 合理设置 session.timeout.ms 和 heartbeat.interval.ms
  4. 集群层面

    • 多副本机制
    • 合理的副本分布策略
    • 定期进行数据校验
    • 监控磁盘使用情况
Kafka 如何不消费重复数据?比如扣款,不能重复扣。

解决重复消费问题的方法:

  1. 幂等性生产者

    • 启用 enable.idempotence=true
    • Kafka 自动处理重试导致的重复消息
    • 局限:仅在单个会话内有效,无法跨会话保证
  2. 事务机制

    • 设置 transactional.id
    • 使用 beginTransaction()send()commitTransaction() 方法
    • 保证跨分区、跨主题的原子性操作
    • 实现 "恰好一次" 语义
  3. 消费端幂等性设计

    • 为每条消息生成唯一标识(UUID、业务 ID 等)
    • 消费前检查消息是否已处理
    • 可使用数据库、缓存(Redis)等存储已处理消息的标识
  4. 状态检查

    • 对于扣款等操作,消费前检查账户状态
    • 基于业务状态决定是否执行操作
  5. 乐观锁

    • 在数据库表中添加版本号字段
    • 更新时检查版本号是否匹配
    • 不匹配则表示已被处理过
Kafka 与传统 MQ 消息系统之间有哪些关键区别?

Kafka 与传统 MQ(如 RabbitMQ、ActiveMQ)的主要区别:

  1. 架构设计

    • Kafka:分布式、分区、多副本
    • 传统 MQ:中心化或主从架构
  2. 性能特性

    • Kafka:高吞吐量、低延迟、支持大规模数据
    • 传统 MQ:中等吞吐量、较高延迟、适合小规模消息
  3. 消息持久化

    • Kafka:基于磁盘的持久化存储
    • 传统 MQ:通常内存存储,可选持久化
  4. 顺序性保证

    • Kafka:分区内严格有序,跨分区需特殊处理
    • 传统 MQ:全局有序,但可能影响性能
  5. 扩展性

    • Kafka:水平扩展能力强
    • 传统 MQ:扩展相对复杂
  6. 消息回溯

    • Kafka:支持任意位置回溯消费
    • 传统 MQ:通常不支持或有限支持
  7. 使用场景

    • Kafka:日志收集、实时数据流处理、事件驱动架构
    • 传统 MQ:企业集成、异步通信、事务消息
  8. 消息处理模型

    • Kafka:基于主题的发布 - 订阅模式,支持消费者组
    • 传统 MQ:支持队列模式、发布 - 订阅模式
  9. 消息确认机制

    • Kafka:灵活的 acks 配置
    • 传统 MQ:更复杂的事务和确认机制
  10. 社区生态

    • Kafka:丰富的流处理生态(Kafka Streams、Connect、REST Proxy)
    • 传统 MQ:成熟的企业集成工具支持

你可能感兴趣的:(java,kafka,spring,cloud)