快速搞定Kafka术语

​ 想要深入学习 Kafka 各种功能和特性,首先肯定要先搞清楚 Kafka 世界中很多概念和术语,所以本文带大家来盘点一下 Kafka 的各种术语。

Kafka 消息层次的概念

​ 在《消息引擎系统》这篇文章中提到过,Kafka 属于分布式的消息引擎系统,它的主要功能是提供一套完备的消息发布与订阅解决方案。在 Kafka 中,发布订阅的对象是主题(Topic),它可以为每个业务、每个应用甚至是每类数据都创建专属的主题。

​ 向主题发布消息的客户端应用程序成为生产者(Producer),生产者程序通常持续不断地向一个或多个主题发送消息,而订阅这些主题消息的客户端应用程序就被称为消费者(Consumer)

​ 有客户端自然也有服务器端。Kafka 的服务器端又被称为 Broker 的服务进程构成,即一个 Kafka 集群由多个 Broker 组成,Broker 负责接收和处理客户端发送过来的请求,以及对消息进行持久化。虽然多个 Broker 进程能够运行在同一台机器上,但是常见的做法是将不同的 Broker 分散运行在不同的机器上,这样如果集群中某一台机器宕机,即使在它上面运行的所有 Broker 进程全部挂掉了,其他机器上的 Broker 进程也能继续对外提供服务。这也是 Kafka 提供高可用的手段之一。

​ 实现高可用的另一个手段就是备份机制(Replication)。备份的思想很简单,就是把相同的数据拷贝到多台机器上,而这些相同的数据拷贝在 Kafka 中被称为副本(Replica)。副本的数量是可以配置的,这些副本保存着相同的数据,但却有不同的角色和作用。Kafka 定义了两类副本:领导者副本(Leader Replica)追随者副本(Follower Replica)。前者对外提供服务,这里的对外指的是与客户端程序进行交互;而后者只是被动地追随领导者副本而已,不能与外界进行交互。

副本的工作机制也很简单:生产者总是向领导着副本写消息;而消费者总是从领导者副本读消息。至于追随者副本,它只做一件事:向领导者副本发送请求,请求领导者把最新生产的消息发给它,这样它能保持与领导者的同步。

​ 虽然有了副本机制可以保证数据的持久化或消息不丢失,但没有解决收缩性的问题。什么是伸缩性呢?拿副本来说,虽然现在有了领导者副本和追随者副本,但倘若领导者副本积累了太多的数据以至于单台的 Broker 机器都无法容纳了,此时应该怎么办呢?一个很自然的想法就是,能否把数据分割成多份保存在不同的 Broker 上?

​ 答案是肯定的,并且 Kafka 就是这样去设计的。这种机制就是所谓的分区(Partitioning)。如果你了解其他分布式系统,你可能听说过分片、分区域等说法,比如MongoDB和Elasticsearch中的Sharding、HBase中的Region,其实它们都是相同的原理,只是Partitioning是最标准的名称。

​ Kafka 中的分区机制指的是将每个主题划分成多个分区(Partition),每个分区是一组有序的消息日志。生产者生产的每条消息只会被发送到一个分区中,也就是说如果向一个双分区的主题发送一条消息,这条消息要么在分区 0 中,要么在分区 1 中。Kafka 的分区编号是从 0 开始的,如果 Topic 有 100 个分区,那么它们的分区号就是从 0 到 99。

​ 再然后就是分区和副本是如何联系在一起的?实际上,副本是在分区这个层级定义的。每个分区下面可以配置若干个副本,其中只能有 1 个领导者副本 和 N - 1 个追随者副本。生产者向分区写入消息,每条消息在分区中的位置信息由一个叫位移(Offset)的数据来表征。分区位移总是从 0 开始,假设一个生产者向一个空分区写入了 10 条消息,那么这 10 条消息的位移分别是0、1、2、…、9.

​ 完整的 Kafka 三层消息架构如下:

  • 第一层是主题层:每个主题可以配置 M 个分区,而每个分区又可以配置 N 个副本。
  • 第二层是分区层:每个分区的 N 个副本中只能有一个充当领导者角色,对外提供服务;其他 N - 1 个副本是追随者副本,只是提供数据冗余之用。
  • 第三层是消息层:分区中包含若干条消息,每条消息的位移从 0 开始,依次递增。
  • 最后,客户端程序只能与分区的领导者副本进行交互。

Kafka Broker 是如何进行持久化的

​ 讲完了消息层次,再来看看 Kafka Broker 是如何进行持久化的。总的来说,Kafka 使用消息日志(Log)来保存数据,一个日志就是磁盘上一个只能追加写(Append-only)消息的物理文件。因为是追加写,故避免了缓慢的随机 I/O 操作,改为了性能较好的顺序 I/O 写操作,这也是实现 Kafka 高吞吃量特性的一个关键手段。随着你不断向一个日志写入消息,最终也会导致耗尽所有的磁盘空间,因此 Kafka 必然要定期删除消息以回收磁盘。删除的方式简单来说,就是通过日志段(Log Segment)机制。在 Kafka 底层,一个日志又进一步细分成多个日志段,消息被追加到当前最新的日志段中,当写满了一个日志段后,Kafka 会自动切分出一个新的日志段,并将老的日志段封存起来。Kafka 在后台还有定时任务会定期地检查老的日志段是否能够被删除,从而实现回收磁盘空间的目的。

​ 这里再重点说说消费者。在专栏的第一期中,我提到过两种消息模型,即点对点模型(Peer to Peer,P2P)发布订阅模型。这里的点对点指的是同一条消息只能被下游的一个消费者消费,其他消费者不能染指。在 kafka 中实现这种 P2P 模型的方法是通过引入消费者组(Consumer Group)来实现的。所谓的消费者组,指的是多个消费者实例共同组成一个组来消费一组主题。这组主题中的不同分区被不同消费者组订阅,每个分区都只会被组内的一个消费者实例消费,其他消费者实例不能消费它,但一个消费者组可以消费多个分区。引入消费者组主要是为了提高消费者端的吞吐量,多个消费者实例同时消费,加速整个消费端的吞吐量(TPS)。这里的消费者实例可以是运行消费者应用的进程,也可以是一个线程,它们都称为一个消费者实例(Consumer Instance)。

​ 消费者组里面的所有消费者实例不仅 “瓜分” 订阅主题的数据,它们还能彼此协助。假设组内某个实例挂掉了,Kafka 能够自动检测到,然后把这个挂掉的实例之前负责的分区转移给其他活着的消费者。这个过程就是Kafka中大名鼎鼎的“重平衡”(Rebalance)。嗯,其实既是大名鼎鼎,也是臭名昭著,因为由重平衡引发的消费者问题比比皆是。事实上,目前很多重平衡的Bug社区都无力解决。

​ 每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上,这个字段就是消费者位移(Consumer Offset)。注意,这和上面所说的位移完全不是一个概念。上面的“位移”表征的是分区内的消息位置,它是不变的,即一旦消息被成功写入到一个分区上,它的位移值就是固定的了。而消费者位移则不同,它可能是随时变化的,毕竟它是消费者消费进度的指示器嘛。另外每个消费者有着自己的消费者位移,因此一定要区分这两类位移的区别。

小结

​ 总结一下上述的所有名词术语:

  • 消息:Record。Kafka 是消息引擎,这里的消息就是指 Kafka 处理的主要对象。
  • 主题:Topic。主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务。
  • 分区:Partition。一个有序不变的消息队列。每个主题下可以有多个分区。
  • 消息位移:Offset。表示分区中每条消息的位置信息,是一个单调递增且不变的值。
  • 副本:Replica。Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。
  • 生产者:Producer。向主题发布新消息的应用程序。
  • 消费者:Consumer。从主题订阅新消息的应用程序。
  • 消费者位移:Consumer Offset。表征消费者消费进度,每个消费者都有自己的消费者位移。
  • 消费者组:Consumer Group。多个消费者实例组成的一个组,同时消费多个分区以实现高吞吐。
  • 重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。

Kafka三层次示意图


快速搞定Kafka术语
http://example.com/2023/11/07/MQ/Kafka/快速搞定Kafka术语/
作者
Feng Tao
发布于
2023年11月7日
更新于
2023年11月7日
许可协议