Kafka源码解读 - 架构及核心概念

Kafka源码解读 - 架构及核心概念

架构设计

Kafka结构

Kafka源码解读 - 架构及核心概念_第1张图片

  • Broker:一台Kafka服务器就是一个Broker,集群由多个Broker组成,一个Broker中保存有多个Topic的不同分区副本,Broker之间没有主从关系。
  • Topic:每条发送到Kafka集群的消息都属于某个主题Topic,物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存在一个或多个Broker上,但从客户角度不需要关心数据存放在何处,只需要指定Topic就可以生产和消费消息。
  • Partition:一个Topic可以分成若干的分区,并保存在集群的多个Broker节点上。分区中的每一条消息都会被分配一个自增ID,即消费位移Offset。Kafka只保证按照一个分区中的顺序将消息发送给消费者,但是不保证多个分区之间的顺序。
  • Offset:分区中的消息消费位移,指向该分区最新消费位移的下一位。
  • Replica:分区的副本,一个分区可以配置多个副本保存在不同Broker节点上,副本集合中有一个Leader副本,其他为Follower副本,Leader副本处理外部读写请求,Follower副本负责同步Leader副本数据。
  • Message:消息是通信的基本单位。
  • Producer:消息生产者,将消息发布到指定Topic,也可以指定发到某个分区,基于轮询或者Hash算法。
  • Consumer:消息消费者,从指定Topic获取并消费消息。
  • Consumer Group:消费者组,当消费者在同一组时,消息在消费者之间负载均衡。一个分区的消息只会被相同消费者组中的某个消费者消费。消费者组之间相互独立。
  • Zookeeper:注册Kafka集群相关元数据的组件。Zookeeper集群中保存了Topic的状态信息,例如分区个数、分区组成、分区分布情况。
Zookeeper节点
  • Topic注册信息:存储Topic的所有分区信息,/brokers/topics/[topic]
  • Partition状态信息:存储分区状态信息,/brokers/topics/[topic]/partitions/[partitionId]/state
  • Broker注册信息:每个Broker都会对应一个全局id,此节点为临时节点/brokers/ids/[0...N]
  • ControllerEpoch信息/controller_epoch,每当controller发生变更,对应的epoch就会递增
  • Controller注册信息/controller,存储controller所在的broker节点信息
  • Consumer Offset/consumers/[groupId]/offsets/[topic]/[partitionId],用来跟踪每个Consumer所消费的分区最大offset,此节点为持久化节点
  • 删除的Topic信息admin/delete_topics,需要删除的Topic信息
  • Topic配置信息/config/topics/[topic_name]

核心概念

分区多副本机制

Kafka利用主题Topic和分区Partition的概念对消息数据进行组织,每个Topic可以有多个分区,而每个分区可以设置多个副本保存在集群的多个Broker节点上。

Kafka引入分区的概念,主要是为了利用分布式系统中的多节点来提升Kafka集群的性能和可扩展性。可以根据Topic的消息读写压力,可以增加或减少分区数来实现动态扩缩容的目的。而分区的多副本机制,则保障了集群的高可用性,这些副本被保存在不同的Broker节点上,副本集合是由一个Leader副本和其他作为Follower副本组合而成,需要注意的是,生产者发送消息和消费者消费消息只与分区的Leader副本交互,Follower副本则不处理任何请求,只负责从Leader副本拉取并同步消息数据。当分区Leader副本失效(Broker宕机等),会从Leader副本中选举出新的Leader副本。

思考:为什么Kafka在设计上只允许Leader副本对外提供服务,而不采用经典的读写分离策略(即允许消费者从Follower副本读取消息)?

读写分离策略在数据库读写应用上较为常见,能够有效解决数据库集群负载压力,但是这适用的场景是读多写少,如果是读少写多的场景,那么主从复制会存在较大延迟,并且数据库的主要压力会几种在master节点,读写分离并不能分摊这部分负载。对Kafka而言,作为消息引擎在大部分场景在是读写对等的,所以读写分离策略的收益并不大,反而会增加设计的复杂度。Kafka的一个Topic的多个分区Leader副本被分散在多个Broker节点上,这已经是负载均衡的一个设计。

AR & ISR

为了保证broker节点宕机时,由该节点管理的topic分区仍然可用,Kafka一般会为topic分区分配多个副本,这些副本成为该Topic分区的Assigned Replicas,简称AR副本集合。

ISR副本集合是指由一个topic分区名下的In-Sync-Replica副本构成的集合。ISR集合是AR的子集,如果AR集合中的副本能够同时满足下列条件,则有资格加入到ISR集合中:

  1. 副本所在broker节点与ZK连接正常,即注册在ZK上;
  2. 副本的LEO值落后于Leader副本LEO值的时间,不超过Broker端参数replica.lag.time.max.ms的值,默认是10s

ISR集合由每个topic分区的Leader副本进行维护,Follower副本在启动后会从Leader副本同步数据消息,当Follower赶上Leader,offset在一定范围后,该Follower副本就有资格被添加进该Topic分区的ISR集合中,ISR集合的个数也由参数控制。与之相反,当Follower副本因一些原因(broker宕机,网络抖动,zookeeper连接异常等)导致消息offset滞后Leader副本超过阈值,那么会被Leader副本从ISR集合中移除。

当Leader副本失效时,会优先从ISR副本集合中竞选出一个成为新的Leader。

思考:为什么采取ISR机制而非一致性协议来保证数据的一致性?

当面临需要维护多个副本的数据一致性时,一般会考虑引入Paxos、Raft这些一致性协议。Kafka不采用一致性协议而采用ISR的原因分析:

  1. Kafka不需要保持所有副本的数据一致性。因为分区只有Leader副本对外提供服务,Follower副本只有在Leader副本失效后成为新的Leader副本才会对外提供服务,平时不会有读请求。如果要维持所有副本一致性会影响到客户端投递消息请求的响应时间,因此收益不高;
  2. 一致性协议通常采用投票机制,如果允许n个副本失效,那么为了保证topic分区能够正常运行,那么需要至少2n+1个副本(失效副本数不能超过总数一半),这样的话topic分区副本数需要至少是3,这样就会增加集群压力。而对于ISR机制来说,只需要n+1个副本即可。
HW & LEO

Kafka源码解读 - 架构及核心概念_第2张图片

HW和LEO是消费位移offset的两个重要指标,HW是High WaterMark,LEO是Log End Offset。

对于每个副本来说,LEO是指向当前最大消费位移offset的下一位的值,当消费者消费完成后,Leader副本的LEO递增,之后Follower副本从Leader副本拉取消息时更新LEO。

HW是有每个分区的Leader副本进行维护,消费者在消费当前topic分区时只能看到HW位置之前的消息。消费者消费完消息之后提交给Leader副本会携带一个acks参数,用于指定是否需要服务端对本次请求进行确认,以及在什么情况下确认。如果acks=-1,则表示需要ISR集合中的所有副本完成对当前消息同步之后,Leader副本才认为此消息被成功记录到集群,此时Leader副本才会递增HW值,一般而言acks不需要设置所有副本都需要完成同步。

思考:HW有什么作用,是怎么帮助Kafka完成副本同步的?

在Kafka中,HW的作用主要有两个:

  1. 定义消息可见性,就是用来标识分区下的哪些消息是可以被消费者消费的;
  2. 帮助Kafka完成副本同步。

虽然所有副本都会保存一组HW和LEO值,但是Kafka是通过Leader副本的HW来定义其所在分区HW的,即分区的HW就是其Leader副本的HW,除此之外,Leader副本所在的Broker还保存了所有Follower副本的LEO值,这也成为远程副本(Remote Replica)。先从HW和LEO的更新说起:

Leader副本处理生产者请求的逻辑:

  1. 写入消息到本地磁盘
  2. 更新分区HW值
    1. 获取Leader副本所在Broker端保存的所有远程副本LEO值,取其中的最小值
    2. 获取Leader副本HW值
    3. 取1.和2.获取到的值中较大的值更新为新的HW值

Leader副本处理Follower副本拉取消息的逻辑:

  1. 读取磁盘(或页缓存)中的消息数据
  2. 使用Follower副本发送请求中的位移值更新远程副本的LEO值
  3. 更新分区HW值

Follower副本从Leader副本拉取消息的处理逻辑:

  1. 写入消息到本地磁盘
  2. 更新自己的LEO值
  3. 更新HW值
    1. 获取Leader发送过来的HW值
    2. 获取2.中更新过的自己的LEO值
    3. 取1.和2.中获取到的较小的值更新HW值

除了利用HW和LEO实现副本同步之外,为了避免Leader和Follower在HW更新时间上错配而导致的数据丢失或数据不一致问题,引入了Leader Epoch的概念:

  1. Epoch是一个递增的版本号,当Leader发生变化时,改值会增加,小于此版本号的Leader副本将不再具有Leader作用
  2. Start Offset,Leader副本在该Epoch值上写入的首条消息的位移。

所以在当Broker宕机恢复时会先根据Leader Epoch判断是否要进行日志截断操作。

总结

对Kafka的整体架构做了一个简单的梳理,Kafka作为一个消息引擎系统,通过主题多分区和零拷贝实现了高并发和大吞吐量,并且将数据以日志形式持久化到磁盘保障消息不丢失。分区多副本机制、ISR机制和高水位HW保障了Kafka集群的高可用性。之后会对Kafka的一些核心模块源码解读,深入理解Kafka是如何运行和管理的。

贝实现了高并发和大吞吐量,并且将数据以日志形式持久化到磁盘保障消息不丢失。分区多副本机制、ISR机制和高水位HW保障了Kafka集群的高可用性。之后会对Kafka的一些核心模块源码解读,深入理解Kafka是如何运行和管理的。

参考
  1. https://www.zhenchao.org/2019/06/17/kafka/kafka-architecture/
  2. https://blog.csdn.net/yuanwei1144/article/details/113544884?spm=1001.2014.3001.5501

你可能感兴趣的:(kafka,架构,big,data)