07 Apache Kafka架构

Posted IT BOY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了07 Apache Kafka架构相关的知识,希望对你有一定的参考价值。

APACHE KAFKA架构

目录

PT1 KAFKA组件介绍

Pt1.1 Broker

Pt1.2 Producer

Pt1.3 Consumer

Pt1.4 Record

Pt1.5 Topic

Pt1.6 Partition

Pt1.7 Replica

Pt1.8 Segment

Pt1.9 Consumer Group

Pt1.10 Consumer Offset

PT2 KAFKA中ZK作用

Pt2.1 元数据管理

Broker注册信息

Topic注册信息

Consumer注册信息

分区与消费者的关系

消息消费进度Offset

Pt2.2 负载均衡

生产者负载均衡

消费者负载均衡

Pt2.3 集群管理和选举


PT1 KAFKA组件介绍

Pt1.1 Broker

和RabbitMQ概念相似,Broker是一台独立的Kafka服务节点,负责消息的存储和转发。

默认端口9092,集群可以包含多个Kafka Broker。

生产者和消费者需要跟Broker建立连接才可以收发消息。

Pt1.2 Producer

消息发送方。在Kafka中,生产者不是逐条发送消息给Broker,而是批量发送的,这样可以提升消息发送速率。通过batch.size参数可以控制批次的大小。

batch.size是Kafka producer非常重要的参数,它的值对Producer的吞吐量有着非常大的影响,因为我们知道,收集到一批消息再发送到broker,比每条消息都请求一次broker,性能会有显著的提高,但 batch.size设置得非常大又会给机器内存带来极大的压力,因此需要在项目中合理地增减batch.size值,才能提高producer的吞吐量。

具体batch.size设置为多大,需要根据实际项目情况,包括单条消息内容大小等因素来判定,通过压测可以得到一个比较合理的值。

Pt1.3 Consumer

消息接收方,消费者。

在RabbitMQ提到过,Consumer消费模式有两种,pull模式和push模式。但和RabbitMQ不同的是,Kafka只支持pull模式,官网对这一点说法是,如果消息发送速度远远大于消费速度,那么push模式下,会有大量消息堆积在消费者,越来越多,直到消费者挂掉位置。

采用Pull模式,未消费的消息保存在Broker中,不会对消费者造成影响,并且可以通过监控的手段预警堆积的消息量,当然如果是堆积在消费者中也可以做监控,但是每台消费者节点都做监控成本就太高,不如堆积在Broker统一做监控。虽然Broker也可能撑不住挂掉,但通过持久化等手段可以保证消息的可靠性,重启后能够继续消费消息。

通过参数可以控制消费者每批次获取多少消息:max.poll.records,默认是500条。

Pt1.4 Record

发送的数据记录,即消息,消息在传输的过程中需要序列化。

消息在Broker中存储格式如下:

 length: varint
 attributes: int8
   bit 0~7: unused
 timestampDelta: varlong
 offsetDelta: varint
 keyLength: varint
 key: byte[]
 valueLen: varint
 value: byte[]
 Headers => [Header]

Pt1.5 Topic

消息队列,生产者发送消息给Topic,消费者从Topic获取消息。生产者和Topic,Topic和消费者,都是多对多关系。

生产者发送消息时,如果Topic不存在,会自动创建Topic。

这个功能也是由参数控制:auto.create.topics.enable,默认为true。

创建Topic脚本:

 kafka-topics.sh --create --zookeeper ip:2181 --replication-factor 1 --partitions 1 --topic spring-kafka-first-topic

Pt1.6 Partition

当Topic压力比较大的时候,会有和单体应用同样的问题:

  1. 在高并发时性能下降非常快。所有客户端都要连到同一个Topic进行消息收发,必然会给Topic带来很大压力,性能会大大下降。

  2. 很难扩展。如果Topic处理能力或者存储能力不足,需要扩展时,只能垂直升级硬件,无法横向扩展。

Kafka通过Partition(分区)来解决这个问题,一个Topic可以被划分成多个分区,每个分区可以对外单独提供消息服务能力,这也是分片思想的落地。Partition类似于MySQL的分库分表,为了达到横向扩展和负载均衡的目的。

一个Topic的不同Partition可以分布在同一个Broker,也可以通过搭建Broker集群部署在不同Broker(横向扩展)。

同一批消息在同一个Partition中能够保证顺序性,Kafka中的消息被消费后不会删除,通过Offset来记录消费位置,消息是被顺序追加到Partition中。

每个Topic在创建时默认至少有一个分区。

使用命令创建分区:

 ./kafka-topics.sh --create --zookeeper ip:2181 --replication-factor 1 --partitions 1 --topic mypartition

--partitions指定分区数,--replication-factor指定副本数。

Pt1.7 Replica

Partition能够提高Topic的吞吐量,但对于每个Partition来说,数据存储都是单机的。如果Broker发生故障,Topic的部分Partition将无法提供服务,分区的数据也无法访问。

为了解决高可用问题,Kafka提供了副本(Replica)机制,每个Partition可以有若干个副本,副本必须分散在不同的Broker(这样副本才有意义,就像异地多机房的原理一样)。

通常指定副本数是包含主节点本身的,生产者和消费者只和Leader节点建立连接,Follower副本从Leader节点同步数据,做冗余来保证高可用。当Leader出现故障时,会从Follower中选举新的Leader节点,以此来保证Partition高可用。

replication-factor可以指定Topic的副本数,在服务端可以通过参数offsets.topic.replication.factor来控制副本数。

使用命令创建副本:

 ./kafka-topics.sh --create --zookeeper ip:2181 --replication-factor 1 --partitions 1 --topic mypartition

--partitions指定分区数,--replication-factor指定副本数。

Pt1.8 Segment

Kafka数据持久化是放在.log的后缀文件中的。如果一个partition只有一个log文件,随着消息越多越多(Kafka消息不删除),文件越来越大,检索效率就越来越低。所以对Kafka持久化数据文件进行切分,切分单位就是段(Segment),默认存储路径为/tmp/kafka-logs。

每个segment至少有1个数据文件和2个索引文件,他们是成套出现的。

通过参数log.segment.bytes可以控制segment文件大小,默认是1G。

segment和log4j的日志很像。

Pt1.9 Consumer Group

前面说过,为了防止消费者被消息压崩,Kafka只支持pull模式,这样消费者就可以根据自身能力消费,消息再多也不会受到影响。但是如果生产者生产消息的速度远远大于消费者的速度,那消息就会在Broker积压,最终影响整个Broker性能,要保证消息生产和消费速度一致,只能增加消费者。

为了方便对消费者进行管理,Kafka引入了Consumer Group的概念,同一个Group的消费者共享同一个Group ID。同一个group中的消费者,不能消费相同的partition,如果想要消费同一个partition消息,需要建立其它Consumer Group来消费。

Pt1.10 Consumer Offset

partition中的消息是有序的,并且在消费之后不会被删除,那如果当前Broker挂了,重启后或者别的Broker重新选主后,怎么知道之前partition消费到哪里了呢,如何防止不会重复消费呢。那就要看Consumer Offset属性。

Kafka __consumer_offsets是一个特殊的存储元数据的Topic。数据格式可以想象成一个 KV 格式的消息,key 就是一个三元组:group.id+topic+分区号,而 value 就是 offset 的值。


PT2 KAFKA中ZK作用

Pt2.1 元数据管理

Broker注册信息

Broker是分布式部署并且相互独立,Zookeeper能够管理和协调集群中各节点。在Zookeeper上会有一个专门用来进行Broker服务器列表记录的节点:/brokers/ids。每个Broker在启动时,都会到Zookeeper上进行注册,即到/brokers/ids下创建属于自己的节点,如/brokers/ids/[0...N]。

Kafka使用了全局唯一的数字来指代每个Broker服务器,不同的Broker必须使用不同的Broker ID进行注册。创建完节点后,每个Broker就会将自己的IP地址和端口信息记录到该节点中去。其中,Broker创建的节点类型是临时节点,一旦Broker宕机,则对应的临时节点也会被自动删除。

Topic注册信息

在Kafka中,同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也都是由Zookeeper在维护,由专门的节点来记录,如:/brokers/topics。Kafka中每个Topic都会以/brokers/topics/[topic]的形式被记录,如/brokers/topics/login和/brokers/topics/search等。

Broker服务器启动后,会到对应Topic节点(/brokers/topics)上注册自己的Broker ID并写入针对该Topic的分区总数,如/brokers/topics/login/3->2,这个节点表示Broker ID为3的一个Broker服务器,对于"login"这个Topic的消息,提供了2个分区进行消息存储,同样,这个分区节点也是临时节点。

Consumer注册信息

消费者服务器在初始化启动时加入消费者分组的步骤如下:

  1. 注册到消费者分组。每个消费者服务器启动时,都会到Zookeeper的指定节点下创建一个属于自己的消费者节点,例如/consumers/[group_id]/ids/[consumer_id],完成节点创建后,消费者就会将自己订阅的Topic信息写入该临时节点。

  2. 对消费者分组中的消费者的变化注册监听。每个消费者都需要关注所属消费者分组中其他消费者服务器的变化情况,即对/consumers/[group_id]/ids节点注册子节点变化的Watcher监听,一旦发现消费者新增或减少,就触发消费者的负载均衡。

  3. 对Broker服务器变化注册监听。消费者需要对/broker/ids/[0-N]中的节点进行监听,如果发现Broker服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者负载均衡。

  4. 进行消费者负载均衡。为了让同一个Topic下不同分区的消息尽量均衡地被多个消费者消费而进行消费者与消息分区分配的过程,通常,对于一个消费者分组,如果组内的消费者服务器发生变更或Broker服务器发生变更,会发出消费者负载均衡。

分区与消费者的关系

消费者组(Consumer Group):消费者组下有多个Consumer(消费者)。

对于每个消费者组(Consumer Group),Kafka都会为其分配一个全局唯一的Group ID,Group内部的所有消费者共享该ID。订阅的topic下的每个分区只能分配给某个group下的一个consumer(当然该分区还可以被分配给其他group)。

同时,Kafka为每个消费者分配一个Consumer ID,通常采用"Hostname:UUID"形式表示。

在Kafka中,规定了每个消息分区只能被同组的一个消费者进行消费,因此,需要在Zookeeper上记录消息分区与Consumer之间的关系,每个消费者一旦确定了对一个消息分区的消费权力,需要将其Consumer ID 写入到 Zookeeper 对应消息分区的临时节点上,例如:/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]。

其中,[broker_id-partition_id]就是一个 消息分区 的标识,节点内容就是该消息分区上消费者的Consumer ID。

消息消费进度Offset

在消费者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度Offset记录到Zookeeper上,以便在该消费者进行重启或者其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。Offset在Zookeeper中由一个专门节点进行记录,其节点路径为:/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id],节点内容就是Offset的值。

Pt2.2 负载均衡

生产者负载均衡

由于同一个Topic消息会被分区并将其分布在多个Broker上,因此,生产者需要将消息合理地发送到这些分布式的Broker上,那么如何实现生产者的负载均衡,Kafka支持传统的四层负载均衡,也支持Zookeeper方式实现负载均衡。

四层负载均衡,根据生产者的IP地址和端口来为其确定相关联的Broker。通常,一个生产者只会对应单个Broker,然后该生产者产生的消息都发往该Broker。这种方式逻辑简单,每个生产者不需要同其他系统建立额外的TCP连接,只需要和Broker维护单个TCP连接即可。但是,其无法做到真正的负载均衡,因为实际系统中的每个生产者产生的消息量及每个Broker的消息存储量都是不一样的,如果有些生产者产生的消息远多于其他生产者的话,那么会导致不同的Broker接收到的消息总数差异巨大,同时,生产者也无法实时感知到Broker的新增和删除。

使用Zookeeper进行负载均衡,由于每个Broker启动时,都会完成Broker注册过程,生产者会通过该节点的变化来动态地感知到Broker服务器列表的变更,这样就可以实现动态的负载均衡机制。

消费者负载均衡

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

Pt2.3 集群管理和选举

负责多个Broker下的partition选举,决定主从关系。

Controller控制器选举。

这部分内容后面会有详细说明。

以上是关于07 Apache Kafka架构的主要内容,如果未能解决你的问题,请参考以下文章

重命名 Apache Apex 应用程序时,Kafka 运算符语义会发生变化吗?

基于事件驱动架构构建微服务第12部分:向Apache KAFKA生成事件

从简单到复杂:Apache Kafka应用实例详解

Apache Kafka:重播主题中的消息

Apache Kafka 是不是适合用作无序任务队列?

从架构层面理解 Apache Pulsar 和 Apache Kafka 不同