责编 | Elle
如果只是为了开拓 Kafka 运用程序,或者只是在生产环境利用 Kafka,那么理解 Kafka 的内部事情事理不是必须的。不过,理解 Kafka 的内部事情事理有助于理解 Kafka 的行为,也利用快速诊断问题。下面我们来磋商一下这三个问题
Kafka 是如何进行复制的

Kafka 是如何处理来自生产者和消费者的要求的
Kafka 的存储细节是若何的
如果感兴趣的话,就请花费你一些韶光,耐心看完这篇文章。
集群成员间的关系
我们知道,Kafka 是运行在 ZooKeeper 之上的,由于 ZooKeeper 因此集议论势涌现的,以是 Kafka 也可以以集议论势涌现。这也就涉及到多个生产者和多个消费者如何折衷的问题,这个掩护集群间的关系也是由 ZooKeeper 来完成的。如果你看过我之前的文章(真的,关于 Kafka 入门看这一篇就够了),你该当会知道,Kafka 集群间会有多个 主机(broker),每个 broker 都会有一个 broker.id,每个 broker.id 都有一个唯一的标识符用来区分,这个标识符可以在配置文件里手动指定,也可以自动天生。
Kafka 可以通过 broker.id.generation.enable 和 reserved.broker.max.id 来合谋生成新的 broker.id。
broker.id.generation.enable参数是用来配置是否开启自动天生 broker.id 的功能,默认情形下为true,即开启此功能。自动天生的broker.id有一个默认值,默认值为1000,也便是说默认情形下自动天生的 broker.id 从1001开始。
Kafka 在启动时会在 ZooKeeper 中 /brokers/ids 路径下注册一个与当前 broker 的 id 相同的临时节点。Kafka 的康健状态检讨就依赖于此节点。当有 broker 加入集群或者退出集群时,这些组件就会得到关照。
如果你要启动其余一个具有相同 ID 的 broker,那么就会得到一个缺点 —— 新的 broker 会试着进行注册,但不会成功,由于 ZooKeeper 里面已经有一个相同 ID 的 broker。
在 broker 停机、涌现分区或者永劫光垃圾回收停顿时,broker 会从 ZooKeeper 上断开连接,此时 broker 在启动时创建的临时节点会从 ZooKeeper 中移除。监听 broker 列表的 Kafka 组件会被奉告该 broker 已移除。
在关闭 broker 时,它对应的节点也会消逝,不过它的 ID 会连续存在其他数据构造中,例如主题的副本列表中,副本列表复制我们下面再说。在完备关闭一个 broker 之后,如果利用相同的 ID 启动另一个全新的 broker,它会急速加入集群,并拥有一个与旧 broker 相同的分区和主题。
Broker Controller 的浸染
我们之前在讲 Kafka Rebalance 重平衡的时候,提过一个群组折衷器,卖力折衷群组间的关系,那么 broker 之间也有一个掌握器组件(Controller),它是 Kafka 的核心组件。它的紧张浸染是在 ZooKeeper 的帮助下管理和折衷全体 Kafka 集群,集群中的每个 broker 都可以称为 controller,但是在 Kafka 集群启动后,只有一个 broker 会成为 Controller 。既然 Kafka 集群是依赖于 ZooKeeper 集群的,以是有必要先先容一下 ZooKeeper 是什么,可以参考作者的这一篇文章(ZooKeeper不仅仅是注册中央,你还知道有哪些?)详细理解,在这里就大略提一下 znode 节点的问题。
ZooKeeper 的数据是保存在节点上的,每个节点也被称为znode,znode 节点是一种树形的文件构造,它很像 Linux 操作系统的文件路径,ZooKeeper 的根节点是 /。
znode 根据数据的持久化办法可分为临时节点和持久性节点。持久性节点不会由于 ZooKeeper 状态的变革而消逝,但是临时节点会随着 ZooKeeper 的重启而自动消逝。
znode 节点有一个 Watcher 机制:当数据发生变革的时候, ZooKeeper 会产生一个 Watcher 事宜,并且会发送到客户端。Watcher 监听机制是 Zookeeper 中非常主要的特性,我们基于 Zookeeper 上创建的节点,可以对这些节点绑定监听事宜,比如可以监听节点数据变更、节点删除、子节点状态变更等事宜,通过这个事宜机制,可以基于 ZooKeeper 实现分布式锁、集群管理等功能。
掌握器的选举
Kafka 当前选举掌握器的规则是:Kafka 集群中第一个启动的 broker 通过在 ZooKeeper 里创建一个临时节点 /controller 让自己成为 controller 掌握器。其他 broker 在启动时也会考试测验创建这个节点,但是由于这个节点已存在,所往后面想要创建 /controller 节点时就会收到一个 节点已存在 的非常。然后其他 broker 会在这个掌握器上注册一个 ZooKeeper 的 watch 工具,/controller 节点发生变革时,其他 broker 就会收到节点变更关照。这种办法可以确保只有一个掌握器存在。那么只有单独的节点一定是有个问题的,那便是单点问题。
如果掌握器关闭或者与 ZooKeeper 断开链接,ZooKeeper 上的临时节点就会消逝。集群中的其他节点收到 watch 工具发送掌握器下线的后,其他 broker 节点都会考试测验让自己去成为新的掌握器。其他节点的创建规则和第一个节点的创建原则同等,都是第一个在 ZooKeeper 里成功创建掌握器节点的 broker 会成为新的掌握器,那么其他节点就会收到节点已存在的非常,然后在新的掌握器节点上再次创建 watch 工具进行监听。
掌握器的浸染
那么说了这么多,掌握是什么呢?掌握器的浸染是什么呢?或者说掌握器的这么一个组件被设计用来干什么?别焦急,接下来我们就要说一说。
Kafka 被设计为一种仿照状态机的多线程掌握器,它可以浸染有下面这几点
掌握器相称于部门(集群)中的部门经理(broker controller),用于管理部门中的部门成员(broker)
掌握器是所有 broker 的一个监视器,用于监控 broker 的上线和下线
在 broker 宕机后,掌握器能够选举新的分区 Leader
掌握器能够和 broker 新选取的 Leader 发送
再细分一下可以详细分为如下 5 点
主题管理 : Kafka Controller 可以帮助我们完成对 Kafka 主题创建、删除和增加分区的操作,简而言之便是对分区拥有最高行使权。
换句话说,当我们实行kafka-topics 脚本时,大部分的后台事情都是掌握器来完成的。
分区重分配: 分区重分配紧张是指,kafka-reassign-partitions 脚本供应的对已有主题分区进行细粒度的分配功能。这部分功能也是掌握器实现的。
Prefered 领导者选举 : Preferred 领导者选举紧张是 Kafka 为了避免部分 Broker 负载过重而供应的一种换 Leader 的方案。
集群成员管理: 紧张管理 新增 broker、broker 关闭、broker 宕机
数据做事: 掌握器的末了一大类事情,便是向其他 broker 供应数据做事。掌握器上保存了最全的集群元数据信息,其他所有 broker 会定期吸收掌握器发来的元数据更新要求,从而更新其内存中的缓存数据。这些数据我们会不才面谈论
当掌握器创造一个 broker 离开集群(通过不雅观察干系 ZooKeeper 路径),掌握器会收到:这个 broker 所管理的那些分区须要一个新的 Leader。掌握器会依次遍历每个分区,确定谁能够作为新的 Leader,然后向所有包含新 Leader 或现有 Follower 的分区发送,该要求包含谁是新的 Leader 以及谁是 Follower 的信息。随后,新的 Leader 开始处理来自生产者和消费者的要求,Follower 用于重新的 Leader 那里进行复制。
这就很像外包公司的一个部门,这个部门便是专门出差的,每个人在不同的地方办公,但是中心总部有一个部门经理,现在部门经理溘然离职了。公司不打算外聘职员,决定从部门内部选一个能力强的人当领导,然后当上领导的人须要向自己的组员发送,这条便是任命和明确他管理了哪些人,大家都知道了,然后再各自给部门干活。
当掌握器创造一个 broker 加入集群时,它会利用 broker ID 来检讨新加入的 broker 是否包含现有分区的副本。如果有掌握器就会把发送给新加入的 broker 和 现有的 broker。
上面这块关于分区复制的内容我们接下来会说到。
broker controller 数据存储
上面我们先容到 broker controller 会供应数据做事,用于保存大量的 Kafka 集群数据。如下图
可以对上面保存信息归类,紧张分为三类
broker 上的所有信息,包括 broker 中的所有分区,broker 所有分区副本,当前都有哪些运行中的 broker,哪些正在关闭中的 broker 。
所有主题信息,包括详细的分区信息,比如领导者副本是谁,ISR 凑集中有哪些副本等。
所有涉及运维任务的分区。包括当前正在进行 Preferred 领导者选举以及分区重分配的分区列表。
Kafka 是离不开 ZooKeeper的,以是这些数据信息在 ZooKeeper 中也保存了一份。每当掌握器初始化时,它都会从 ZooKeeper 上读取对应的元数据并添补到自己的缓存中。
broker controller 故障转移
我们在前面说过,第一个在 ZooKeeper 中的 /brokers/ids下创建节点的 broker 作为 broker controller,也便是说 broker controller 只有一个,那么一定会存在单点失落效问题。kafka 为考虑到这种情形供应了故障转移功能,也便是 Fail Over。如下图
最一开始,broker1 会抢先注册成功成为 controller,然后由于网络抖动或者其他缘故原由致使 broker1 掉线,ZooKeeper 通过 Watch 机制觉察到 broker1 的掉线,之后所有存活的 brokers 开始竞争成为 controller,这时 broker3 抢先注册成功,此时 ZooKeeper 存储的 controller 信息由 broker1 -> broker3,之后,broker3 会从 ZooKeeper 中读取元数据信息,并初始化到自己的缓存中。
把稳:ZooKeeper 中存储的不是缓存信息,broker 中存储的才是缓存信息。
broker controller 存在的问题在 Kafka 0.11 版本之前,掌握器的设计是相称繁琐的。我们上面提到过一句话:Kafka controller 被设计为一种仿照状态机的多线程掌握器,这种设计实在是存在一些问题的
controller 状态的变动由不同的监听器并发实行,因此须要进行很繁芜的同步,并且随意马虎出错而且难以调试。
状态传播不同步,broker 可能在韶光不愿定的情形下涌现多种状态,这会导致不必要的额外的数据丢失。
controller 掌握器还会为主题删除创建额外的 I/O 线程,导致性能损耗。
controller 的多线程设计还会访问共享数据,我们知道,多线程访问共享数据是线程同步最麻烦的地方,为了保护数据安全性,掌握器不得不在代码中大量利用ReentrantLock 同步机制,这就进一步拖慢了全体掌握器的处理速率。
broker controller 内部设计事理
在 Kafka 0.11 之后,Kafka controller 采取了新的设计,把多线程的方案改成了单线程加事宜行列步队的方案。如下图所示
紧张所做的改变有下面这几点
第一个改进是增加了一个 Event Executor Thread,事宜实行线程,从图中可以看出,不管是 Event Queue 事宜行列步队还是 Controller context 掌握器高下文都会交给事宜实行线程进行处理。将原来实行的操作全部建模成一个个独立的事宜,发送到专属的事宜行列步队中,供此线程消费。
第二个改进是将之前同步的 ZooKeeper 全部改为异步操作。ZooKeeper API 供应了两种读写的办法:同步和异步。之前掌握器操作 ZooKeeper 都是采取的同步办法,这次把同步办法改为异步,据测试,效率提升了10倍。
第三个改进是根据优先级处理要求,之前的设计是 broker 会公正性的处理所有 controller 发送的要求。什么意思呢?公正性难道还不好吗?在某些情形下是的,比如 broker 在排队处理 produce 要求,这时候 controller 发出了一个 StopReplica 的要求,你会怎么办?还在连续处理 produce 要求吗?这个 produce 要求还有用吗?此时最合理的处理顺序该当是,授予 StopReplica 要求更高的优先级,使它能够得到抢占式的处理。
副本机制
复制功能是 Kafka 架构的核心功能,在 Kafka 文档里面 Kafka 把自己描述为 一个分布式的、可分区的、可复制的提交日志做事。复制之以是这么关键,是由于的持久存储非常主要,这能够担保在主节点宕机后依旧能够担保 Kafka 高可用。副本机制也可以称为备份机制(Replication),常日指分布式系统在多台网络交互的机器上保存有相同的数据备份/拷贝。
Kafka 利用主题来组织数据,每个主题又被分为多少个分区,分区会支配在一到多个 broker 上,每个分区都会有多个副本,以是副本也会被保存在 broker 上,每个 broker 可能会保存成千上万个副本。下图是一个副本复制示意图
如上图所示,为了大略我只画出了两个 broker ,每个 broker 指保存了一个 Topic 的,在 broker1 等分区0 是Leader,它卖力进行分区的复制事情,把 broker1 中的分区0复制一个副本到 broker2 的主题 A 的分区0。同理,主题 A 的分区1也是一样的道理。
副本类型分为两种:一种是 Leader(领导者) 副本,一种是Follower(跟随者)副本。
Leader 副本
Kafka 在创建分区的时候都要选举一个副本,这个选举出来的副本便是 Leader 领导者副本。
Follower 副本
除了 Leader 副本以外的副本统称为 Follower 副本,Follower 不对外供应做事。下面是 Leader 副本的事情办法
这幅图须要把稳以下几点
Kafka 中,Follower 副本也便是追随者副本是不对外供应做事的。这便是说,任何一个追随者副本都不能相应消费者和生产者的要求。所有的要求都是由领导者副本来处理。或者说,所有的要求都必须发送到 Leader 副当地点的 broker 中,Follower 副本只是用作数据拉取,采取异步拉取的办法,并写入到自己的提交日志中,从而实现与 Leader 的同步
当 Leader 副当地点的 broker 宕机后,Kafka 依托于 ZooKeeper 供应的监控功能能够实时感知到,并开启新一轮的选举,从追随者副本中选一个作为 Leader。如果宕机的 broker 重启完成后,该分区的副本会作为 Follower 重新加入。
首领的另一个任务是搞清楚哪个跟随者的状态与自己是同等的。跟随者为了担保与领导者的状态同等,在有新到达之前先考试测验从领导者那里复制。为了与领导者保持同等,跟随者向领导者发起获取数据的要求,这种要求与消费者为了读取消息而发送的信息是一样的。
跟随者向领导者发送的过程是这样的,先要求1,然后再吸收到1,在时候到要求1之后,发送要求2,在收到领导者给发送给跟随者之前,跟随者是不会连续发送的。这个过程如下
跟随者副本在收到相应前,是不会连续发送,这一点很主要。通过查看每个跟随者要求的最新偏移量,首领就会知道每个跟随者复制的进度。如果跟随者在10s 内没有要求任何,或者虽然跟随者已经发送要求,但是在10s 内没有收到,就会被认为是不同步的。如果一个副本没有与领导者同步,那么在领导者掉线后,这个副本将不会称为领导者,由于这个副本的不是全部的。
与之相反的,如果跟随者同步的和领导者副本的同等,那么这个跟随者副本又被称为同步的副本。也便是说,如果领导者掉线,那么只有同步的副本能够称为领导者。
关于副本机制我们说了这么多,那么副本机制的好处是什么呢?
能够急速看到写入的,便是你利用生产者 API 成功向分区写入后,立时利用消费者就能读取刚才写入的
能够实现的幂等性,啥意思呢?便是对付生产者产生的,在消费者进行消费的时候,它每次都会看到存在,并不会存在不存在的情形
同步复制和异步复制
我在学习副本机制的时候,有个疑问,既然领导者副本和跟随者副本是发送 - 等待机制的,这是一种同步的复制办法,那么为什么说跟随者副本同步领导者副本的时候是一种异步操作呢?
我认为是这样的,跟随者副本在同步领导者副本后会把保存在本地 log 中,这个时候跟随者会给领导者副本一个相应,见告领导者自己已经保存成功了,同步复制的领导者会等待所有的跟随者副本都写入成功后,再返回给 producer 写入成功的。而异步复制是领导者副本不须要关心跟随者副本是否写入成功,只办法导者副本自己把保存到本地 log ,就会返回给 producer 写入成功的。下面是同步复制和异步复制的过程
同步复制
producer 关照 ZooKeeper 识别领导者
producer 向领导者写入
领导者收到后会把写入到本地 log
跟随者会从领导者那里拉取消息
跟随者向本地写入 log
跟随者向领导者发送写入成功的
领导者会收到所有的跟随者发送的
领导者向 producer 发送写入成功的
异步复制
和同步复制的差异在于,领导者在写入本地log之后,直接向客户端发送写入成功,不须要等待所有跟随者复制完成。
ISR
Kafka动态掩护了一个同步状态的副本的凑集(a set of In-Sync Replicas),简称ISR,ISR 也是一个很主要的观点,我们之前说过,追随者副本不供应做事,只是定期的异步拉取领导者副本的数据而已,拉取这个操作就相称于是复制,ctrl-c + ctrl-v大家肯定用的熟。那么是不是说 ISR 凑集中的副本的数量都会与领导者副本数量一样呢?那也不一定,判断的依据是 broker 中参数 replica.lag.time.max.ms 的值,这个参数的含义便是跟随者副本能够掉队领导者副本最长的韶光间隔。
replica.lag.time.max.ms 参数默认的韶光是 10秒,如果跟随者副本掉队领导者副本的韶光不超过 10秒,那么 Kafka 就认为领导者和跟随者是同步的。纵然此时跟随者副本中存储的要小于领导者副本。如果跟随者副本要掉队于领导者副本 10秒以上的话,跟随者副本就会从 ISR 被剔除。倘若该副本后面逐步地追上了领导者的进度,那么它是能够重新被加回 ISR 的。这也表明,ISR 是一个动态调度的凑集,而非静态不变的。
Unclean 领导者选举
既然 ISR 是可以动态调度的,那么一定会涌现 ISR 凑集中为空的情形,由于领导者副本是一定涌如今 ISR 凑集中的,那么 ISR 凑集为空一定解释领导者副本也挂了,以是此时 Kafka 须要重新选举一个新的领导者,那么该如何选举呢?现在你须要转变一下思路,我们上面说 ISR 凑集中一定是与领导者同步的副本,那么不再 ISR 凑集中的副本一定是不与领导者同步的副本了,也便是不再 ISR 列表中的跟随者副本会丢失一些。如果你开启 broker 端参数 unclean.leader.election.enable的话,下一个领导者就会在这些非同步的副本中选举。这种选举也叫做Unclean 领导者选举。
如果你打仗过分布式项目的话你一定知道 CAP 理论,那么这种 Unclean 领导者选举实在是捐躯了数据同等性,担保了 Kafka 的高可用性。
你可以根据你的实际业务场景决定是否开启 Unclean 领导者选举,一样平常不建议开启这个参数,由于数据的同等性要比可用性主要的多。
Kafka 要求处理流程
broker 的大部分事情是处理客户端、分区副本和掌握器发送给分区领导者的要求。这种要求一样平常都是要求/相应式的,我预测你打仗最早的要求/相应的办法该当便是 HTTP 要求了。事实上,HTTP 要求可以是同步可以是异步的。一样平常正常的 HTTP 要求都是同步的,同步办法最大的一个特点是提交要求->等待做事器处理->处理完毕返回 这个期间客户端浏览器不能做任何事。而异步办法最大的特点是 要求通过事宜触发->做事器处理(这时浏览器仍旧可以做其他事情)-> 处理完毕。
那么我也可以说同步要求便是顺序处理的,而异步要求的实行办法则不愿定,由于异步须要创建多个实行线程,而每个线程的实行顺序不同。
这里须要把稳一点,我们只是利用 HTTP 要求来举例子,而 Kafka 采取的是 TCP 基于 Socket 的办法进行通讯
那么这两种办法有什么缺陷呢?
我相信聪明的你该当能立时想到,同步的办法最大的缺陷便是吞吐量太差,资源利用率极低,由于只能顺序处理要求,因此,每个要求都必须等待前一个要求处理完毕才能得到处理。这种办法只适用于要求发送非常不频繁的系统。
异步的办法的缺陷便是为每个要求都创建线程的做法开销极大,在某些场景下乃至会压垮全体做事。
相应式模型
说了这么半天,Kafka 采取同步还是异步的呢?都不是,Kafka 采取的是一种 相应式(Reactor)模型,那么什么是相应式模型呢?大略的说,Reactor 模式是事宜驱动架构的一种实现办法,特殊适宜运用于处理多个客户端并发向做事器端发送要求的场景,如下图所示
Kafka 的 broker 端有个 SocketServer组件,类似于处理器,SocketServer 是基于 TCP 的 Socket 连接的,它用于接管客户端要求,所有的要求都包含一个头,头中都包含如下信息
Request type (也便是 API Key)
Request version(broker 可以处理不同版本的客户端要求,并根据客户版本做出不同的相应)
Correlation ID --- 一个具有唯一性的数字,用于标示要求,同时也会涌如今相应和缺点日志中(用于诊断问题)
Client ID --- 用于标示发送要求的客户端
broker 会在它所监听的每一个端口上运行一个 Acceptor 线程,这个线程会创建一个连接,并把它交给 Processor(网络线程池), Processor 的数量可以利用 num.network.threads 进行配置,其默认值是3,表示每台 broker 启动时会创建3个线程,专门处理客户端发送的要求。
Acceptor 线程会采取轮询的办法将入栈要求公正的发送至网络线程池中,因此,在实际利用过程中,这些线程常日具有相同的机率被分配到待处理要求行列步队中,然后从相应行列步队获取相应,把它们发送给客户端。Processor 网络线程池中的要求 - 相应的处理还是比较繁芜的,下面是网络线程池中的处理流程图
Processor 网络线程池吸收到客户和其他 broker 发送来的后,网络线程池会把放到要求行列步队中,把稳这个是共享要求行列步队,由于网络线程池是多线程机制的,以是要求行列步队的是多线程共享的区域,然后由 IO 线程池进行处理,根据的种类判断做何处理,比如 PRODUCE 要求,就会将写入到 log 日志中,如果是FETCH要求,则从磁盘或者页缓存中读取消息。也便是说,IO线程池是真正做判断,处理要求的一个组件。在IO 线程池处理完毕后,就会判断是放入相应行列步队中还是 Purgatory 中,Purgatory 是什么我们下面再说,现在先说一下相应行列步队,相应行列步队是每个线程所独占的,由于相应式模型中不会关心要求发往何处,因此把相应回传的事情就交给每个线程了,以是也就不必共享了。
把稳:IO 线程池可以通过 broker 端参数 num.io.threads 来配置,默认的线程数是8,表示每台 broker 启动后自动创建 8 个IO 处理线程。
要求类型下面是几种常见的要求类型
生产要求
我在 真的,关于 Kafka 入门看这一篇就够了 文章中提到过 acks 这个配置项的含义
大略来讲便是不同的配置对写入成功的界定是不同的,如果 acks = 1,那么只办法导者收到就表示写入成功,如果acks = 0,表示只办法导者发送就表示写入成功,根本不用考虑返回值的影响。如果 acks = all,就表示领导者须要收到所有副本的后才表示写入成功。
在被写入分区的首领后,如果 acks 配置的值是 all,那么这些要求会被保存在 炼狱(Purgatory)的缓冲区中,直到领导者副本创造跟随者副本都复制了,相应才会发送给客户端。
获取要求
broker 获取要求的办法与处理生产要求的办法类似,客户端发送要求,向 broker 要求主题分区中特定偏移量的,如果偏移量存在,Kafka 会采取 零复制 技能向客户端发送,Kafka 会直接把从文件中发送到网络通道中,而不须要经由任何的缓冲区,从而得到更好的性能。
客户端可以设置获取要求数据的上限和下限,上限指的是客户端为接管足够分配的内存空间,这个限定比较主要,如果上限太大的话,很有可能直接耗尽客户端内存。下限可以理解为攒足了数据包再发送的意思,这就相称于项目经理给程序员分配了 10 个bug,程序员每次改一个 bug 就会向项目经理申报请示一下,有的时候改好了有的时候可能还没改好,这样就增加了沟通本钱和韶光本钱,以是下限值得便是程序员你改完10个 bug 再向我申报请示!
!
!
如下图所示
如图你可以看到,在拉取消息 ---> 之间是有一个等待积累这么一个过程的,这个积累你可以把它想象成超时时间,不过超时会跑出非常,积累超时后会相应回执。延迟韶光可以通过 replica.lag.time.max.ms 来配置,它指定了副本在复制时可被许可的最大延迟韶光。
元数据要求
生产要乞降相应要求都必须发送给领导者副本,如果 broker 收到一个针对某个特定分区的要求,而该要求的首领在其余一个 broker 中,那么发送要求的客户端会收到非分区首领的缺点相应;如果针对某个分区的要求被发送到不含有领导者的 broker 上,也会涌现同样的缺点。Kafka 客户端须要把要乞降相应发送到精确的 broker 上。这不是废话么?我怎么知道要往哪发送?
事实上,客户端会利用一种 元数据要求 ,这种要求会包含客户端感兴趣的主题列表,做事真个相应指明了主题的分区,领导者副本和跟随者副本。元数据要求可以发送给任意一个 broker,由于所有的 broker 都会缓存这些信息。
一样平常情形下,客户端会把这些信息缓存,并直接向目标 broker 发送生产要乞降相应要求,这些缓存须要隔一段韶光就进行刷新,利用metadata.max.age.ms 参数来配置,从而知道元数据是否发生了变更。比如,新的 broker 加入后,会触发重平衡,部分副本会移动到新的 broker 上。这时候,如果客户端收到 不是首领的缺点,客户端在发送要求之前刷新元数据缓存。
Kafka 重平衡流程
我在 真的,关于 Kafka 入门看这一篇就够了 中关于消费者描述的时候大致说了一下消费者组和重平衡之间的关系,实际上,归纳为一点便是让组内所有的消费者实例就消费哪些主题分区达成同等。
我们知道,一个消费者组中是要有一个群组折衷者(Coordinator)的,而重平衡的流程便是由 Coordinator 的帮助下来完成的。
这里须要先声明一下重平衡发生的条件
消费者订阅的任何主题发生变革
消费者数量发生变革
分区数量发生变革
如果你订阅了一个还尚未创建的主题,那么重平衡在该主题创建时发生。如果你订阅的主题发生删除那么也会发生重平衡
消费者被群组折衷器认为是 DEAD 状态,这可能是由于消费者崩溃或者永劫光处于运行状态下发生的,这意味着在配置合理韶光的范围内,消费者没有向群组折衷器发送任何心跳,这也会导致重平衡的发生。
在理解重平衡之前,你须要知道这两个角色
群组折衷器(Coordinator):群组折衷器是一个能够从消费者群组中收到所有消费者发送心跳的 broker。在最早期的版本中,元数据信息是保存在 ZooKeeper 中的,但是目前元数据信息存储到了 broker 中。每个消费者组都该当和群组中的群组折衷器同步。当所有的决策要在运用程序节点中进行时,群组折衷器可以知足 JoinGroup 要求并供应有关消费者组的元数据信息,例如分配和偏移量。群组折衷器还有权知道所有消费者的心跳,消费者群组中还有一个角色便是领导者,把稳把它和领导者副本和 kafka controller 进行区分。领导者是群组中卖力决策的角色,以是如果领导者掉线了,群组折衷器有权把所有消费者踢出组。因此,消费者群组的一个很主要的行为是选举领导者,并与折衷器读取和写入有关分配和分区的元数据信息。
消费者领导者:每个消费者群组中都有一个领导者。如果消费者停滞发送心跳了,折衷者会触发重平衡。
在理解重平衡之前,你须要知道状态机是什么
Kafka 设计了一套消费者组状态机(State Machine) ,来帮助折衷者完玉成部重平衡流程。消费者状态机紧张有五种状态它们分别是 Empty、Dead、PreparingRebalance、CompletingRebalance 和 Stable。
理解了这些状态的含义之后,下面我们用几条路径来表示一下消费者状态的轮转
消费者组一开始处于 Empty 状态,当重平衡开启后,它会被置于 PreparingRebalance 状态等待新消费者的加入,一旦有新的消费者加入后,消费者群组就会处于 CompletingRebalance 状态等待分配,只要有新的消费者加入群组或者离开,就会触发重平衡,消费者的状态处于 PreparingRebalance 状态。等待分配机制指定好后完身分派,那么它的流程图是这样的
在上图的根本上,当消费者群组都到达 Stable 状态后,一旦有新的消费者加入/离开/心跳过期,那么触发重平衡,消费者群组的状态重新处于 PreparingRebalance 状态。那么它的流程图是这样的。
在上图的根本上,消费者群组处于 PreparingRebalance 状态后,很不幸,没人玩儿了,所有消费者都离开了,这时候还可能会保留有消费者消费的位移数据,一旦位移数据过期或者被刷新,那么消费者群组就处于 Dead 状态了。它的流程图是这样的
在上图的根本上,我们剖析了消费者的重平衡,在 PreparingRebalance或者 CompletingRebalance 或者 Stable 任意一种状态下发生位移主题分区 Leader 发生变更,群组会直接处于 Dead 状态,它的所有路径如下
这里面须要把稳两点:
一样平常涌现 Required xx expired offsets in xxx milliseconds 就表明Kafka 很可能就把该组的位移数据删除了只有 Empty 状态下的组,才会实行过期位移删除的操作。重平衡流程上面我们理解到了消费者群组状态的转化过程,下面我们真正开始先容 Rebalance 的过程。重平衡过程可以从两个方面去看:消费者端和折衷者端,首先我们先看一下消费者端从消费者看重平衡从消费者看重平衡有两个步骤:分别是 消费者加入组 和 等待领导者分配方案。这两个步骤后分别对应的要求是 JoinGroup 和 SyncGroup。新的消费者加入群组时,这个消费者会向折衷器发送 JoinGroup 要求。在该要求中,每个消费者成员都须要将自己消费的 topic 进行提交,我们上面描述群组折衷器中说过,这么做的目的便是为了让折衷器网络足够的元数据信息,来选取消费者组的领导者。常日情形下,第一个发送 JoinGroup 要求的消费者会自动称为领导者。领导者的任务是网络所有成员的订阅信息,然后根据这些信息,制订详细的分区消费分配方案。如图
在所有的消费者都加入进来并把元数据信息提交给领导者后,领导者做出分配方案并发送 SyncGroup要求给折衷者,折衷者卖力下发群组中的消费策略。下图描述了 SyncGroup 要求的过程
当所有成员都成功吸收到分配方案后,消费者组进入到 Stable 状态,即开始正常的消费事情。
从折衷者来看重平衡从折衷者角度来看重平衡紧张有下面这几种触发条件,
新成员加入组
组成员主动离开
组成员崩溃离开
组成员提交位移
我们分别来描述一下,先重新成员加入组开始新成员入组我们谈论的场景消费者集群状态处于Stable 等待分配的过程,这时候如果有新的成员加入组的话,重平衡的过程
从这个角度来看,折衷者的过程和消费者类似,只是刚刚从消费者的角度去看,现在从领导者的角度去看
组成员离开组成员离开消费者群组指的是消费者实例调用 close 方法主动关照折衷者它要退出。这里又会有一个新的要求涌现 LeaveGroup要求 。如下图所示
组成员崩溃组成员崩溃是指消费者实例涌现严重故障,宕机或者一段韶光未相应,折衷者吸收不到消费者的心跳,就会被认为是组成员崩溃,崩溃离组是被动的,折衷者常日须要等待一段韶光才能感知到,这段韶光一样平常是由消费者端参数 session.timeout.ms 掌握的。如下图所示
重平衡时提交位移这个过程我们就不再用图形来表示了,大致描述一下便是 消费者发送 JoinGroup 要求后,群组中的消费者必须在指定的韶光范围内提交各自的位移,然后再开启正常的 JoinGroup/SyncGroup 要求发送。声明:本文为作者投稿,版权归作者个人所有。作为多年 PHP 的开拓者,在利用了 Go 措辞之后......