Kafka设计架构与原理

2022-09-07 09:56:22

Kafka基本介绍

Kafka是一个分布式消息系统或是一个分布式流式处理平台,具有高吞吐、持久化、水平扩展、流数据处理(Spark、Storm、Flink等)等多种特性。

Kafka整体设计是典型的发布与订阅系统,且没有”中心主节点“概念,集群中所有的服务器器都是对等的,因此可以在不做任何配置修改的情况下实现服务器的添加与删除。

  1. 消息系统:Kafka和消息中间件都具备系统解耦、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等功能。
    与此同时,Kafka还提供了大多数消息系统难以实现的消息顺序性保障及回溯消费的功能。
  2. 存储系统:Kafka把消息持久化到磁盘,相对基于内存的存储系统而言,有效地降低了数据丢失的风险。
    基于Kafka的消息持久化和多副本机制,可以把Kafka作为长期的数据存储系统来使用,只需要修改对应的数据保存策略为”永久“或启用主体的日志压缩功能即可。
  3. 流式处理平台:Kafka不仅为每个流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理类库,如窗口、连接、变换和聚合等操作。

基本概念与整体架构

一个典型的Kafka架构包括Producer(生产者)、Broker(代理)、Consumer(消费者),以及ZooKeeper集群。

其中ZooKeeper负责集群元数据管理、控制器的选举等操作。

架构图在这里插入图片描述

Producer(生产者)

负责生产消息,并发送到Kafka中。

Consumer(消费者)

负责消费Kafka上消息,并进行相应的业务逻辑处理。

Broker(服务代理节点)

Broker可以简单地看做一个独立的Kafka服务器节点,用于存储消息。

Topic(主题)

Kafka以主题为单位进行归类,生产者负责将消息发送到特定的主题(Topic),而消费者负责订阅主题(Topic)并消费。

一个主题可以跨越多个Broker(服务代理节点),以支持负载均,实现更好的性能。

Partition(消息分区)

主题是一个逻辑概念,它可以细分为多个分区,一个分区只属于单个主题。

同一主题下的不同分区包含的消息是不同的,分区在存储层面可以看做一个可追加的日志文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(Offset)。

每一条消息被发送到broker之前,会根据分区规则选择存储到哪个具体的分区。如果分区设置的合理,所有的消息可以均匀地分布在不同的分区中。如果一个主题只有对应一个文件,那么这个文件所在机器I/O将会成为这个主题的信你瓶颈。

Group(消费者分组)

用于归类消费同一主题的消费者,在Kafka中,多个消费者消费可以共同消费一个主题(Topic)下的消息,每个消费者消费其中部分消息,这些消费者就组成了一个分区,拥有同一个分组名称,通常也被称为消费者集群。

Offset(文件偏移量)

offset是消息在分区中的唯一标识,Kafka通过它保证消息在分区的顺序,Offset不能跨分区。即Kafka保证是分区有序而不是主题有序。

消费者者拉取消息数据的过程中需要知道消息在文件中的偏移量。

ZooKeepr管理Kafka

Kafka通过ZooKeeper来管理元数据,以及实现Kafka的高可用,以及负载均衡。

Broker(代理服务节点)注册

Broker是分布式部署并且相互之间是独立运行的,所以需要一个注册系统能够将真个集群中的Broker服务器都管理起来。Kafka选择了ZooKeeper进行管理所有Broker。

在ZooKeeper上会创建节点路径为/broker/ids来专门记录Broker服务器列表。每个Broker服务器在启动时,都会到ZooKeeper上进行注册,创建属于自己的临时节点,其节点路径为/broker/ids/[0…N]。

Kafka使用了一个全局唯一的数字来指定每个Broker服务器,可以称其为”Broker ID“,不同Brokder必须使用不同的Broker ID进行注册。

通过临时节点的特性,一旦这个Broker服务器宕机或下线,对应的节点会自动删除。因此可以通过ZooKeeper上Broker节点的变化情况来动态表征Broker服务器的可用性。

Topic(主题)注册

在Kafka中,会将同一个主题(Topic)的消息分成多个分区将其分布到多个Broker上,而这些分区消息以及与Broker的对应关系也需要由ZooKeeper维护,有专门的”Topic 节点“节点来记录,其节点路径为/brokers/topics。

Kafka中的每个主题(Topic),都会以/broker/topics/[topic]的形式记录在这个节点下,如:/broker/topics/login。

Broker服务器在启动后,会到对应的Topic节点下注册自己的Broker ID,并写入针对该Topic的分区总数。

例如:/broker/topics/login/3 -> 2这个节点表名Broker服务器的 Broker ID为3,对于”login“这个主题(Topic)的消息,提供了2个分区进行消息存储。同时这个节点也是临时节点。

生产者负载均衡

Kafka是分布式部署Broker服务器的,会对同一个Topic的消息进行分区并将其分布到不同的Broker服务器上。因此生产者需要将消息合理地发送到这些分布式的Broker上----这就面临一个问题:如和进行生产者的负载均衡。
Kafka支持传统的四层负载均衡,也同时支持使用ZooKeeper方式实现负载均衡。

四层负载均衡

根据生成者的IP地址和端口来为其确定一个相关的Broker。通常一个生产者只会对应单个Broker,然后所有消息都发送给这个Broker。

优点:整体逻辑简单,不需要引入其他三方系统,同时每个生成者也不需要同其他系统建立额外的TCP链接,只需要和Broker维护单个TCP链接即可。

切点:无法做到真正的负载均衡。在实现运行环境中,每个生成者产生的消息量,以及每个Broker的消息存储量都是不一样的,如果有些生产者产生消息远多于其他生产者,那么会导致不同的Broker接受到的消息总数非常不均匀。
另一方面,生产者也无法实时感知到Broker的新装与删除,因此,这种负载均衡方式无法做到动态的负载均衡。

使用ZooKeeper进行负载均衡

在Kafka中,客户端使用了基于ZooKeeper的负载均衡策略来解决生产者的负载均衡问题。

每当一个Broker启动时,会首先完成Broker注册过程,并注册一些诸如”有哪些可订阅的Topic“的元是数据信息。生产者就能够通过这个节点变化来动态地感知到Broker服务器列表的变更。

Kafka的生产者会对ZooKeeper上的”Broker的新增与减少“、”Topic的新增与减少“和“Broker与Topic关联的变化”等事件注册Watcher监听,这样就可以实现动态的负载均衡机制。

这种模式下,还可以允许开发人员控制生产者根据一定的规则(例如根据消费者的消费行为)来进行分区,而不仅仅是随机算法而已------Kafka将这种特定的分区策略称为“语义分区”。

通过ZooKeeper和Watcher通知能够让生产者动态获取Broker和Topic的变化请情况。

消费者负载均衡

Kafka中的消费者需要进行负载均衡来实现多个消费者合理地从对应的Broker服务器上接收消息。Kafka有消息分组的概念,每个消费者分组中包含若干个消费者,每一条消息都只会发送给分组中一个消费者不同的消费者分组消息自己特定主题(Topic)下面的消息,互不干扰。

消费分区与消费者关系

对于每个消费者分区,Kafka都会为其分配一个全局唯一的Group ID,同一个消费者分组内部的所有消息都共享该ID,同时,Kafka也会为每个消息者分配一个Consumer(消费者) ID,通常采用“Hostname:UUID”的形式表达。

Kafka的设计中,规范了每个消息分区有且只能同时一个消费者进行消息的消费。因此,需要在ZooKeeper上记录消息分区与消息者之间的关系。每个消息者一但确定了一个消息分区的消费权利,那么将其Consumer(消费者) ID写入到对应消息分区的临时节点上。

例如:/consumer/[group_id]/owners/[topic]/[broker_id-partition_id],其中“[broker_id-partition_id]”就是一个消息分区的标识,节点内容就是消费该分区的消费者Customer ID。

消息消费进度Offset记录

消息者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度。即Offset记录到ZooKeeper上去,以便在改消费者宕机或是其他消息者重新接管该消息分区的消息消费后,能够从之间的进度开始继续进行消息的消费。

Offset在ZooKeeper上的记录有一个专门的节点负责,其节点路径为/customer/[group_id]/offsets/[topic]/[broker_id-partition_id],其节点内容就是Offset值。

消费者注册

消费者服务器在初始化穷时加入消费者分组的过程。

  1. 注册到消费者分区
    每个消费者服务器器在启动的时候,都会到ZooKeeper的指定节点下创建一个属于自己的消费者节点,例如:/customers/[group_id]/ids/[consumer_id]。
    完成节点创建后,消费者就会将自己订阅的主题(Topic)信息写入该节点。注意,这个节点也是临时节点,也就是说,一旦消费者服务器出现宕机或下线后,其对应节点就会被删除。
  2. 对消费者分区中消费者的变换注册监听。
    每个消费者都需要关注所属消费者分组中消费者服务器的变化情况,即对/consumers/[group_id]/ids节点注册子节点变化的Watcher监听。一旦发现消费者新增或减少,就会触发消费者的负载均衡。
  3. 对Broker服务器变化注册监听
    消费者需要对/brokers/ids/[0…N]中节点进行监听注册,如果发现Broker服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者的负载均衡。
  4. 进行消费者负载均衡
    为了能够让同一个主题(Topic)下不同的分区的消息尽量均衡地别多个消费者消费而进行一个消费者与消费分区分配的过程。通过对于一个消费者分组,如果组内的消费者服务器发生变更或Broker服务器发生变更,会触发消费者负载均衡。

负载均衡

Kafka借助ZooKeeper上记录的Broker和消费者信息,采用了一套特殊的消费者负载均衡算法。
将一个消费者分组的每个消费者记录为C1,C2—CG,那么对于一个消费者Ci,其对应消息分区的分配策略如下:

  1. 设置PT为指定主题(Topic)所有的消息分区
  2. 设置CG为同一个消费者分组中的所有消费者。
  3. 对于PT进行排序,使分布在同一个Broker服务器的分区尽量靠在一起。
  4. 对CG进行排序
  5. 设置i为Ci在CG中位置的索引值,同时设置N=size(PT)/size(CG)。
  6. 将编号为i*N~(i+1)*N -1的消费分区分配给消费者Ci
  7. 重新更新ZooKeeper上消费分区与消费者Ci的关系。
  • 作者:swg321321
  • 原文链接:https://blog.csdn.net/swg321321/article/details/126328098
    更新时间:2022-09-07 09:56:22