Kafka原理篇:图解kakfa架构原理

show88me
发布于 2022-4-17 21:39
浏览
0收藏

作者 | MageByte技术团队

来源 | 码哥字节(ID:MageByte)

转载请联系授权(微信ID:MageByte1024)

这是[码哥]Kafka 系列文章的第二篇,码哥将从原理、实践和源码角度为大家深入剖析并实践 Kafka。此系列包括[原理篇]、[实践篇]和[源码篇]。这篇是[原理篇]的第二篇,主要讲解 Kafka 的架构和实现原理。

读者可以回顾之前的文章《Kafka 性能篇:为何 Kafka 这么"快"?》。

今天我们来深入讲解 Kafka 的架构和实现原理。[码哥]将从架构和细节入手,以生动的图深入讲解 Kafka 的实现原理。

我想很多同学之前可能已经看过很多 Kafka 原理相关的文章,但往往看时"牛逼"声连连,激情满满,总觉得自己又学习到了各种“吊炸天”的技术。但很多同学往往是不觉明厉,把文章结合面试题背一背还能应付一下半吊子面试官。可以遇到老司机面试官,或是进入实战,却对很多概念和实现摸棱两可。

所以,[码哥]决定图解 Kakfa,却让很多半懂不懂的同学可以加深对 Kafka 实现原理的理解。

同时建议读者同学结合 Kafka 的配置去了解 Kafka 的实现原理,Kafka 有大量的配置,这也是 Kafka 高度扩展的一个表现,很多同学对 Kafka 的配置也不敢轻易改动。所以理解这些配置背后的实现原理,可以让我们在实践中懂得如何使用和优化 Kafka。既可面试造火箭,也可以实战造火箭。

Kafka 配置说明链接:https://kafka.apache.org/documentation

下面是本文的主要的内容:Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

由于内容太多,怕步子迈太大扯着蛋,[码哥]决定将文章分成三篇。此文只会涉及上面图中"橙色"的部分。

从本文你将学习到:

  • Kafka 架构设计哲学和原理
  • Kafka 中 zookeeper 的作用
  • Kafka Controller 实现原理
  • Kafka Network 原理

开篇寄语

尽可能做一些产品出来,有一个作品很重要,这是别人了解你的窗口。如果可能,给自己开一个公众号或者一个博客,记录自己每天的见闻思考。刚开始记会很凌乱没有逻辑,但坚持下去一定会有很大价值。

Architecture

理解 Kafka 架构,就是理解 Kafka 的各种组件的概念,以及这些组件的关系。先简单看一下各组件及其简单说明。

不要去尝试记忆他们

Producer: 生产者,发送消息的一方。生产者负责创建消息,然后将其发送到 Kafka。

Consumer: 消费者,接受消息的一方。消费者连接到 Kafka 上并接收消息,进而进行相应的业务逻辑处理。

Consumer Group: 一个消费者组可以包含一个或多个消费者。使用多分区 + 多消费者方式可以极大提高数据下游的处理速度,同一消费组中的消费者不会重复消费消息,同样的,不同消费组中的消费者消息消息时互不影响。Kafka 就是通过消费组的方式来实现消息 P2P 模式和广播模式。

Broker: 服务代理节点。Broker 是 Kafka 的服务节点,即 Kafka 的服务器。

Topic: Kafka 中的消息以 Topic 为单位进行划分,生产者将消息发送到特定的 Topic,而消费者负责订阅 Topic 的消息并进行消费。

Partition: Topic 是一个逻辑的概念,它可以细分为多个分区,每个分区只属于单个主题。同一个主题下不同分区包含的消息是不同的,分区在存储层面可以看作一个可追加的日志(Log)文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)。

Offset: offset 是消息在分区中的唯一标识,Kafka 通过它来保证消息在分区内的顺序性,不过 offset 并不跨越分区,也就是说,Kafka 保证的是分区有序性而不是主题有序性。

Replication: 副本,是 Kafka 保证数据高可用的方式,Kafka 同一 Partition 的数据可以在多 Broker 上存在多个副本,通常只有主副本对外提供读写服务,当主副本所在 broker 崩溃或发生网络异常,Kafka 会在 Controller 的管理下会重新选择新的 Leader 副本对外提供读写服务。

Record: 实际写入 Kafka 中并可以被读取的消息记录。每个 record 包含了 key、value 和 timestamp。

我们理解了也就自然记住了

我们应该通过理解的方式去记忆它们。

生产者-消费者

生产者-消费者是一种设计模式,生产者和消费者之间通过添加一个中间组件来达到解耦。生产者向中间组件生成数据,消费者消费数据。

就像 65 哥读书时给小芳写情书,这里 65 哥就是生产者,情书就是消息,小芳就是消费者。但有时候小芳不在,或者比较忙,65 哥也比较害羞,不敢直接将情书塞小芳手里,于是将情书塞在小芳抽屉中。所以抽屉就是这个中间组件。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

在程序中我们通常使用Queue来作为这个中间组件。可以使用多线程向队列中写入数据,另外的消费者线程依次读取队列中的数据进行消费。模型如下图所示:Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

生产者-消费者模式通过添加一个中间层,不仅可以解耦生产者和消费者,使其易于扩展,还可以异步化调用、缓冲消息等。

分布式队列

后来 65 哥和小芳异地了,65 哥在卷都奋斗,小芳在魔都逛街。于是只能通过邮局寄暧昧信了。这样 65 哥、邮局和小芳就成了分布式的了。65 哥将信件发给邮局,小芳从邮局拿到 65 哥写的信,再回去慢慢看。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

Kafka 的消息生产者就是Producer,上游消费者进程添加 Kafka Client 创建 Kafka Producer,向 Broker 发送消息,Broker 是集群部署在远程服务器上的 Kafka Server 进程,下游消费者进程引入 Kafka Consumer API 持续消费队列中消息。

因为 Kafka Consumer 使用 Poll 的模式,需要 Consumer 主动拉去消息。所有小芳只能定期去邮局拿信件了(呃,果然主动权都在小芳手上啊)。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

主题

邮局不能只为 65 哥服务,虽然 65 哥一天写好几封信。但也无法挽回邮局的损失。所以邮局是可以供任何人寄信。只需要寄信人写好地址(主题),邮局建有两地的通道就可以发收信件了。

Kafka 的 Topic 才相当于一个队列,Broker 是所有队列部署的机器。可以按业务创建不同的 Topic,Producer 向所属业务的 Topic 发送消息,相应的 Consumer 可以消费并处理消息。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

分区

由于 65 哥写的信太多,一个邮局已经无法满足 65 哥的需求,邮政公司只能多建几个邮局了,65 哥将信件按私密度分类(分区策略),从不同的邮局寄送。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

同一个 Topic 可以创建多个分区。理论上分区越多并发度越高,Kafka 会根据分区策略将分区尽可能均衡的分布在不同的 Broker 节点上,以避免消息倾斜,不同的 Broker 负载差异太大。分区也不是越多越好哦,毕竟太多邮政公司也管理不过来。具体的原因可以参考[码哥]之前的文章《Kafka 性能篇:为何 Kafka 这么"快"?》

副本

为防止由于邮局的问题,比如交通断啦,邮车没油啦。导致 65 哥的暧昧信无法寄到小芳手上,使得 65 哥晚上远程跪键盘。邮局决定将 65 哥的信件复制几份发到多个正常的邮局,这样只要有一个邮局还在,小芳就可以收到 65 哥的信了。

Kafka 采用分区副本的方式来保证数据的高可用,每个分区都将建立指定数量的副本数,kakfa 保证同一分区副本尽量分布在不同的 Broker 节点上,以防止 Broker 宕机导致所有副本不可用。Kafka 会为分区的多个副本选举一个作为主副本(Leader),主副本对外提供读写服务,从副本(Follower)实时同步 Leader 的数据。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

多消费者

哎,65 哥的信件满天飞,小芳天天跑邮局,还要一一拆开看,65 哥写的信又臭又长,让小芳忙得满身大汉大汗。于是小芳啪的一下,很快啊,变出多个分身去不同的邮局取信,这样小芳终于可以挤出额外的时间逛街了。

广播消息

邮局最近提供了定制明信片业务,每个人都可以设计明信片,同一个身份只能领取一种明信片。65 哥设计了一堆,广播给所有漂亮的小妹妹都可以来领取,美女啪变出的分身也可以来领取,但是同一个身份的多个分身只能取一种明信片。

Kafka 通过 Consumer Group 来实现广播模式消息订阅,即不同 group 下的 consumer 可以重复消费消息,相互不影响,同一个 group 下的 consumer 构成一个整体。

最后我们完成了 Kafka 的整体架构,如下:Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

Zookeeper

Zookeeper 是一个成熟的分布式协调服务,它可以为分布式服务提供分布式配置服、同步服务和命名注册等能力.。对于任何分布式系统,都需要一种协调任务的方法。Kafka 是使用 ZooKeeper 而构建的分布式系统。但是也有一些其他技术(例如 Elasticsearch 和 MongoDB)具有其自己的内置任务协调机制。

Kafka 将 Broker、Topic 和 Partition 的元数据信息存储在 Zookeeper 上。通过在 Zookeeper 上建立相应的数据节点,并监听节点的变化,Kafka 使用 Zookeeper 完成以下功能:

  • Kafka Controller 的 Leader 选举
  • Kafka 集群成员管理
  • Topic 配置管理
  • 分区副本管理

我们看一看 Zookeeper 下 Kafka 创建的节点,即可一目了然的看出这些相关的功能。Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

Controller

Controller 是从 Broker 中选举出来的,负责分区 Leader 和 Follower 的管理。当某个分区的 leader 副本发生故障时,由 Controller 负责为该分区选举新的 leader 副本。当检测到某个分区的 ISR(In-Sync Replica)集合发生变化时,由控制器负责通知所有 broker 更新其元数据信息。当使用kafka-topics.sh脚本为某个 topic 增加分区数量时,同样还是由控制器负责分区的重新分配。

Kafka 中 Contorller 的选举的工作依赖于 Zookeeper,成功竞选为控制器的 broker 会在 Zookeeper 中创建/controller这个临时(EPHEMERAL)节点。

选举过程Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

Broker 启动的时候尝试去读取/controller节点的brokerid的值,如果brokerid的值不等于-1,则表明已经有其他的 Broker 成功成为 Controller 节点,当前 Broker 主动放弃竞选;如果不存在/controller节点,或者 brokerid 数值异常,当前 Broker 尝试去创建/controller这个节点,此时也有可能其他 broker 同时去尝试创建这个节点,只有创建成功的那个 broker 才会成为控制器,而创建失败的 broker 则表示竞选失败。每个 broker 都会在内存中保存当前控制器的 brokerid 值,这个值可以标识为 activeControllerId。

实现Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

Controller 读取 Zookeeper 中的节点数据,初始化上下文(Controller Context),并管理节点变化,变更上下文,同时也需要将这些变更信息同步到其他普通的 broker 节点中。Controller 通过定时任务,或者监听器模式获取 zookeeper 信息,事件监听会更新更新上下文信息,如图所示,Controller 内部也采用生产者-消费者实现模式,Controller 将 zookeeper 的变动通过事件的方式发送给事件队列,队列就是一个LinkedBlockingQueue,事件消费者线程组通过消费消费事件,将相应的事件同步到各 Broker 节点。这种队列 FIFO 的模式保证了消息的有序性。

职责

Controller 被选举出来,作为整个 Broker 集群的管理者,管理所有的集群信息和元数据信息。它的职责包括下面几部分:

  1. 处理 Broker 节点的上线和下线,包括自然下线、宕机和网络不可达导致的集群变动,Controller 需要及时更新集群元数据,并将集群变化通知到所有的 Broker 集群节点;
  2. 创建 Topic 或者 Topic 扩容分区,Controller 需要负责分区副本的分配工作,并主导 Topic 分区副本的 Leader 选举。
  3. 管理集群中所有的副本和分区的状态机,监听状态机变化事件,并作出相应的处理。Kafka 分区和副本数据采用状态机的方式管理,分区和副本的变化都在状态机内会引起状态机状态的变更,从而触发相应的变化事件。

65 哥:状态机啊,听起来好复杂。

Controller 管理着集群中所有副本和分区的状态机。大家不要被状态机这个词唬住了。理解状态机很简单。先理解模型,即这是什么关于什么模型,然后就是模型的状态有哪些,模型状态之间如何转换,转换时发送相应的变化事件。

Kafka 的分区和副本状态机很简单。我们先理解,这分别是管理 Kafka Topic 的分区和副本的。它们的状态也很简单,就是 CRUD,具体说来如下:

分区状态机

PartitionStateChange,管理 Topic 的分区,它有以下 4 种状态:

  1. NonExistentPartition:该状态表示分区没有被创建过或创建后被删除了。
  2. NewPartition:分区刚创建后,处于这个状态。此状态下分区已经分配了副本,但是还没有选举 leader,也没有 ISR 列表。
  3. OnlinePartition:一旦这个分区的 leader 被选举出来,将处于这个状态。
  4. OfflinePartition:当分区的 leader 宕机,转移到这个状态。

我们用一张图来直观的看看这些状态是如何变化的,以及在状态发生变化时 Controller 都有哪些操作:Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

副本状态机

ReplicaStateChange,副本状态,管理分区副本信息,它也有 4 种状态:

  1. NewReplica: 创建 topic 和分区分配后创建 replicas,此时,replica 只能获取到成为 follower 状态变化请求。
  2. OnlineReplica: 当 replica 成为 parition 的 assingned replicas 时,其状态变为 OnlineReplica, 即一个有效的 OnlineReplica。
  3. OfflineReplica: 当一个 replica 下线,进入此状态,这一般发生在 broker 宕机的情况下;
  4. NonExistentReplica: Replica 成功删除后,replica 进入 NonExistentReplica 状态。

副本状态间的变化如下图所示,Controller 在状态变化时会做出相应的操作:Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

Network

Kafka 的网络通信模型是基于 NIO 的 Reactor 多线程模型来设计的。其中包含了一个Acceptor线程,用于处理新的连接,Acceptor 有 N 个 Processor 线程 select 和 read socket 请求,N 个 Handler 线程处理请求并相应,即处理业务逻辑。下面就是 KafkaServer 的模型图:Kafka原理篇:图解kakfa架构原理-鸿蒙开发者社区

之后的 Kafka 源码篇,[码哥]将从源码的角度来讲解这些原理在代码上的具体实现,各位敬请期待啊。

收藏
回复
举报
回复
    相关推荐