《Kafka权威指南》记录

初识 Kafka

消息和批次

消息有一个可选的元数据,也称为键。键是一个字节数组,可以用来实现如分布式键等应用。

为了提高效率,消息会被分批次写入 Kafka,批次包含了一组同属于一个主题和分区的消息。

模式

模式就是消息的格式,如Json、XML。

主题和分区

消息通过主题进行分类,主题又被分为若干个分区。

生产者和消费者

默认情况下,生产者会把消息均衡地分布到主题的所有分区中,也可以通过自定义设置决定消息发布到哪里。

消费者通过检查消息的偏移量来区分已经读取过的消息。

broker 和集群

一台单独的 Kafka 服务器称为 broker,单个 broker 可以处理数千个分区和每秒百万级的消息量。

集群由 broker 组成,同时也有集群中都有的分布式共识算法。Kafka 使用 ZAB 作为共识算法,2.8 版本后支持 KRaft 管理元数据。

Kafka 与其他消息队列的区别

Kafka 与其他消息队列相比,有以下几个显著的区别:

  1. 高吞吐量与低延迟
    • Kafka 设计上支持高吞吐量和低延迟,能够处理海量数据和高并发写入、读取场景。
    • 其他消息队列(如 RabbitMQ、ActiveMQ)通常适用于较小规模或实时性要求较高的场景,但在海量数据和高并发场景下可能会遇到性能瓶颈。
  2. 持久化与分布式存储
    • Kafka 使用磁盘持久化和日志分段技术,并通过分区(Partition)和副本(Replica)机制实现分布式存储和容错。
    • 其他消息队列有的采用内存队列(例如 RabbitMQ 默认使用内存),虽然也支持持久化,但在大规模数据存储和高可靠性需求下不如 Kafka 灵活。
  3. 消息模型与消费模式
    • Kafka 基于发布-订阅模型,采用拉取(Pull)的消费模式。消费者主动拉取消息,可以灵活控制消费速率和消息重放。
    • 其他消息队列(例如 RabbitMQ)多采用推送(Push)模式,消息直接推送给消费者,实时性较好,但可能在处理慢消费者时需要额外机制(如 ACK、流控等)。
  4. 数据持久性和消息重放
    • Kafka 中的消息存储在日志中,并且消息不会在被消费后自动删除(可以根据保留策略删除),这使得消费者可以根据需求重放消息。
    • 其他消息队列通常在消息被消费确认后就删除,无法轻易重放历史消息。
  5. 扩展性与容错性
    • Kafka 天生支持分布式集群部署,通过增加 Broker 节点来横向扩展,且内置副本机制保证容错。
    • 其他消息队列虽然也有集群部署方案,但在大规模扩展和高容错要求下往往不如 Kafka 那样简单高效。
  6. 应用场景不同
    • Kafka 更适用于日志收集、流处理、大数据实时分析等场景。
    • 其他消息队列则更适合需要复杂路由、即时响应、任务队列等场景,比如电商下单、实时通知等。

Kafka 生产者

消息发布方式:

  • 发送并忘记 (fire-and-forget)
  • 同步发送 (发送返回一个 Future 对象,调用 get()方法等待,就知道是否发送成功)
  • 异步发送 (发送并指定一个回调函数,服务器在返回响应时调用该函数)

Kafka 消费者

消费者概念

Kafka 消费者从属于消费者群组。一个群组里的消费者订阅的是同一个主题,每个消费者接收主题一部分分区的消息。但是不要让消费者数量超过分区数量,多余消费者会闲置。

分区所有权从一个消费者转移到另一个消费者,称之为分区再均衡。

消费者通过向被指派为群组协调器的 broker(不同群组可以有不同的协调器)发送心跳来维持它们和群组的从属关系以及它们对分区的所有权关系。消费者轮询消息(为了获取消息)或者提交偏移量时发送心跳。如果消费者停止发送心跳的时间足够长,会话就会过期,群组协调器就认为其已经死亡,触发一次再均衡。

提交和偏移量

把更新分区当前位置的操作叫做提交。消费者往一个叫做 _consumer_offset 的特殊主题发送消息, 消息里包含每个分区的偏移量。如果消费者一直处于运行状态,偏移量就没啥用处。 如果消费者崩溃或者有新的消费者加入群组,就会触发再均衡,完成均衡以后每个消费者可能分配到新分区, 为了能继续之前工作,消费者需要读取每个分区最后一次提交的偏移量,然后从偏移量指定地方继续处理。

如果提交的偏移量小于客户端处于的最后一个消息的偏移量,那么处于两个偏移量之间的消息会被重复处理。 如果提交的偏移量大于客户端处理的最后一个消息的偏移量,俩偏移量之间的消息将会丢失。

深入 Kafka

集群成员关系

Kafka 使用 Zookeeper 维护集群成员关系,kafka 组件订阅 Zookeeper 的 /brokers/ids 路径,当有 broker 加入或者退出集群的时候,这些组件就得到通知。

控制器

控制器其实就是一个 broker,只不过还负责分区选举。控制器使用 epoch 避免脑裂, 脑裂指的是两个节点同时认为自己是控制器。

复制

  • 首领副本:每个分区都有一个首领副本,为了保持一致性,所有生产者请求和消费者请求都会经过这个副本。

  • 跟随者副本:唯一的任务就是从首领复制消息,保持和首领一致的状态。如果首领崩溃,其中一个被提升为新首领。

物理存储

每个 Topic 划分为多个分区(Partition),分区是物理存储的基本单元。每个分区对应一个有序、不可变的消息序列,以追加写入(Append-only)的方式存储。每个分区的日志被拆分为多个段文件(Segment),默认大小由 segment.bytes (如1GB)控制。当段达到阈值,会创建新段,旧段只读。

每个日志段对应一个 .index 文件,存储偏移量到物理位置的映射(稀疏索引),默认每隔index.interval.bytes(如4KB)创建一条索引,加速消息定位。.timeindex 文件记录时间戳到偏移量的映射,支持按时间范围快速检索。