kafka 的基本组成与机制

2022-06-27 14:25:18 浏览数 (1)

1. 引言

2016 年的文章中,我们详细介绍过 rabbitmq,他是一种实现了 AMQP 队列协议的消息队列,具有非常强大的多种功能: rabbitmq 实战

随着业务数据量的提升,rabbitmq 的消费性能将可能成为系统的瓶颈,而 kafka 以其高吞吐量渐渐被越来越多的人接受,那么,kafka 是如何构成的,又是如何做到有如此高的吞吐量的呢?接下来我们就来详细了解一下。

本文主要总结自《深入理解 kafka:核心设计与实践原理》的第一章

2. 简介

Kafka 是 LinkedIn 公司为解决数据管道通信的性能问题而基于 ZooKeeper 开发的一个多分区、多副本的分布式消息系统。 后来,Kafka 被捐给了 Apache 基金会进行维护。 Kafka 以其高吞吐、可持久化、可水平扩展、支持流数据处理等稻种特性而被广泛使用,在实际使用中,他可以扮演三大角色:

  1. 消息系统 — 作为消息中间件,Kafka 具备很多消息中间件所共有的系统解耦、冗余存储、流量削峰、缓冲、异步通信、可扩展性、可恢复性等功能,他的消息回溯机制是其他很多消息中间件所不具备的
  2. 存储系统 — Kafka 将消息持久化到磁盘中,相比于其他内存型消息队列,有效降低数据丢失风险,如果将 Kafka 数据保留策略设置为“永久”或启用主题的日志压缩功能,Kafka 甚至可以作为长期的存储系统来使用
  3. 流式处理平台 — Kafka 提供了一个完整的流式处理类库,很多开源分布式处理系统如 C loudera、Storm、Spark、Flink 等都原生支持了与 Kafka 的集成

3. kafka 的构成

3.1. 代理节点 — Broker

在 Rabbitmq 的介绍中,我们讲解了 Broker 在集群中的作用: 分布式架构 Broker 简介

和 RabbitMQ 一样,Kafka 也是通过 Broker 来组织的。 在 kafka 中,每个 Broker 就是 kafka 的一个代理节点,简单的来说就是一个 kafka 服务节点。 一般来说,在一个 kafka 集群中,每个机器上都只部署一个实例,那么,这个机器就可以看作是一个 Broker。 就这样,多个 Broker 就组成了一个 Kafka 集群。 Broker 在 zookeeper 的协调下完成 Kafka 集群协作的同时,也承担着消息的存储。

3.2. 生产者与消费者 — Producer & Consumer

作为一个消息队列组件,Producer 与 Consumer 当然是最为重要的两个组成部分,也就是消息的发送方与接收方。 如上图所示,Producer 将数据发送到 Broker 上,由 Broker 进行存储,Consumer 则连接到 Broker 上进行拉取和消费数据。 多个 Consumer 可以组成一个消费组来统一进行管理。

3.3. 主题和分区 — Topic & Partition

Kafka 中,消息以 Topic 为单位进行归类,Producer 将消息发送到特定的 Topic 上,而 Consumer 则在启动时需要订阅某个主题并进行消费。 Topic 是由若干个分区组成的,每个分区都只能属于单个的主题,事实上,Topic 只是逻辑上的概念,而分区才是 Topic 借以实现的实体。 每个分区可以看作是一个可追加的日志文件,每个消息都拥有自己在分区中的偏移量 offset,而在每个分区中,消息的 offset 就成为了消息的唯一标识,依赖 offset,kafka 实现了在单个分区内的消息有序性,可以理解,一个分区中若干条消息的消费是按照消息的 offset 有序的,而在一个 Topic 中,消息的消费是无序的。

3.3.1. 通过 Topic 和 Partition 实现高吞吐

Topic 是一个逻辑概念,因此他可以跨越多个 Broker,但每个分区则只能存在于某个 Broker 上,被一个 Consumer Group 内的一个 Consumer 进行消费,但反之,一个 Consumer 是可以同时消费多个分区的。

每个 Broker 上可以拥有很多个分区,每当一条消息被发送到 broker 之前,都会根据分区规则选择存储到哪个具体的分区,最为合理的规则是让消息可以均匀的分配到不同的分区中,这样,多个机器共同承担一个 Topic 的读写,同一个机器上,多个分区 log 文件同时承担他们所对应的 Topic 的读写,就可以让整个集群的 IO 性能大幅提升。

3.3.2. 通过 Topic 和 Partition 实现高可用

如同其他许多存储系统,多副本备份也是 Kafka 重要的特性,每个 leader 副本分区都有若干个 follower 副本分区分布在其他 Broker 上。 当某个 Broker 发生故障,Kafka 基于“一主多从”的副本同步机制,可以轻松的将某个 follower 副本分区作为 leader 分区,实现故障的自动转移和容灾,从而保障整个集群的高可用状态。

4. 容灾和恢复机制

和很多其他存储系统一样,follower 副本分区的数据相对于 leader 分区可能存在一定的滞后,因此在 Broker 故障自动转以后,可能会造成部分消息的丢失,下面,我们具体来介绍一下分区同步的机制。 在 Kafka 集群中,所有的分区分为 leader 副本和 follower 副本,他们共同组成了 AR(Assigned Replicas),而 leader 副本与所有保持一定程度同步的 follower 副本则组成 ISR(In-Sync Replicas),剩下的 follower 副本则是超过阈值时间没有进行过同步的副本,他们组成了 OSR(Out-of-Sync Replicas) 所有的消息总是会由 leader 副本分区进行处理,而 follower 副本分区则需要定时从 leader 副本分区进行拉取。 每个 leader 副本分区都维护了 ISR、OSR 两个集合,一旦 ISR 中的某个副本超过了阈值时间没有进行拉取操作,那么这个副本就会从 ISR 中被移到 OSR 中,相反,如果 OSR 中的某个 follower 副本分区进行了消息的拉取,那么他就会被移到 ISR 中。 当故障发生时,默认情况下只有 ISR 中的副本才有资格被选为新的 leader 副本,通过将参数 unclean.leader.election.enable 设置为 true 可以改变这个默认行为,从而让 OSR 也有资格被选为新的 leader,不过通常不建议这么做,因为这意味着故障恢复中可能丢失更多的数据。 在正常情况下,消息一旦被 leader 副本处理完成就会立即返回 Producer 发送消息成功,而只有 ISR 中所有 follower 副本都已经完成这条消息的拉取,这条消息才能够被 Consumer 进行消费,此处的 leader 副本中存储的最新消息 offset 就是“高水位线”,而 ISR 中最早完成同步的 follower 副本中的最新消息 offset 就是“低水位线”。 默认情况下,故障发生时,高低水位线之间的消息是有可能丢失的。

5. 消费机制

之前我们介绍过,Rabbitmq 实现了 AMQP 消息服务应用协议中指定的六种通信机制,与之相比,kafka 的通信机制就显得更为简单。 正如我们上文提到的,在 Kafka 中,所有消息都保存在 Broker 的分区上,每个 Consumer 定期到自己订阅的 Topic 中进行拉取,并自行维护自己拉取的分区中已处理消息的偏移。

5.1. 优点

这样的好处非常明显,数据被 kafka 集群统一存储,不存在其他消息队列组件常见的消息积压、流控等问题,而由于消费者各自维护他所关心的每个分区的消息 offset,也避免了消费者与消息队列组件间反复通信来更改消息消费状态的性能损耗以及一致性问题。 同时,消费者可以在下次拉取数据前更改自己所维护的 offset,从而实现消息队列消息到任意时间节点的回溯。

5.2. 缺点

拉取的模式同样存在着一些缺点。

  1. 消费者采用轮询的方式访问集群,集群承受更大的压力,需要在集群搭建前充分评估
  2. 轮询的消费方式造成消费实时性的下降,不适用于实时性要求过高的场景

6. 参考资料

《深入理解 kafka:核心设计与实践原理》。

0 人点赞