引自: https://www.confluent.io/blog/kafka-without-zookeeper-a-sneak-peek/
Kafka 的核心是日志——一种简单的数据结构,它使用与底层硬件共生的顺序操作。Kafka 的设计是以日志为中心,这样带来了高效的磁盘缓冲和 CPU 缓存使用率、预取、零拷贝数据传输以及许多其他好处,从而带来了显著的高效率和吞吐量。对于那些刚接触 Kafka 的人来说,他们通常要了解的第一件事就是 topic 及其作为提交 commitLog 的底层实现。
但是日志本身的代码在整个系统中所占的比例相对较小。Kafka 的代码库中有很大一部分负责调整分区(即日志),这些分区跨一个集群中多个 broker 节点、分配领导权、处理失败等。这些代码使 Kafka 成为可靠、可信的分布式系统。
在过去,apache zookeeper 是这个分布式系统工作方式的关键部分。ZooKeeper 提供了元数据的权威存储,其中包含了系统最重要的真实状态:分区所在的位置、哪个副本是领导者等等。ZooKeeper 的使用在早期是有意义的,它是一个强大且经验证的工具。但归根结底,ZooKeeper 是一个在连续日志之上的有点怪异的文件系统/触发器 API。Kafka 是一个基于连续日志之上的发布/订阅 API。这使得操作系统的人员可以跨越两个日志实现、两个网络层和两种安全实现,对通信和性能进行调优、配置、监控、保护和判断,每个实现都有不同的工具和监视 hook。它变得复杂,其实没必要。这种固有的、不可避免的复杂性促使最近达成一项倡议:使用完全运行在 Kafka 里面的内部仲裁服务取代 ZooKeeper。
当然,更换 Zookeeper 是一项相当大的工作,去年 4 月,我们启动了一项社区倡议,以加快进度,并在年底前提供一个可工作的系统。
我只是和 Jason,Colin 和 KIP-500 团队坐在一起,经历了一个完整的 Kafka 服务器生命周期,生产,消费和其他所有的组件免 Zookeeper 依赖!— Ben Stopford (@benstopford) [2020 年 12 月 15 日]
因此,我们非常高兴地说,KIP-500代码的早期访问已经提交给trunk,并有望包含在即将发布的2.8版本中。首次,你可以不依赖zk运行kafka。我们称之为Kafka Raft元数据模式,通常简称为KRaft(发音类似craft)模式。1.
复制代码
请注意,有些功能在此早期版本中不可用。我们还不支持使用 ACL 和其他安全功能或事务。另外,在 KRaft 模式下,分区重新分配和 JBOD 都不予支持(预计今年晚些时候 apache kafka 版本中会提供这些功能)。因此,采用基于 Raft 共识协议的仲裁控制器机制,我们不建议将它用于生产工作环境。不过,如果你真的试用了这个软件,你会发现许多新的优点:部署和操作更简单,你可以将 Kafka 作为一个进程整体运行,它可以为每个集群容纳更多的分区(参见下面的度量)。
如果选择使用新的仲裁控制器运行 Kafka,则以前由 Kafka 控制器和 ZooKeeper 承担的所有元数据职责都将合并到这个新服务中,在 Kafka 集群内部运行。如果用例中有需要,仲裁控制器也可以运行在专有硬件上。
然而,从内部来看,这很有趣。仲裁控制器使用新的 KRaft 协议,确保在仲裁中准确地复制元数据。这个协议在许多方面与 ZooKeeper 的 ZAB 协议和 Raft 协议相似,但是有一些重要的区别,一个值得注意的区别是它使用了事件驱动架构。
仲裁控制器使用事件源存储模型存储其状态,这可确保内部状态机始终可以被准确地重建。事件日志用于存储此状态(也称为元数据主题),会被快照定期删节,以确保日志不会无限增长。仲裁中的其他控制器通过以下方式来同步活动控制器:响应日志中的创建和存储事件。因此,如果一个节点由于分区事件而暂停,例如,当它重新加入时,它可以通过访问日志来快速赶上它错过的任何事件。这大大减少了服务不可用窗口,改善了系统最坏情况下的恢复时间。
KRaft 协议的事件驱动特性意味着,与基于 ZooKeeper 的控制器不同,仲裁控制器不需要从 ZooKeeper 加载状态来变成活动状态。当 leader 节点发生变化时,新的活动控制器已经在内存中拥有所有提交的元数据记录。此外,KRaft 协议中使用的事件驱动机制也用于跨集群跟踪元数据。以前用 RPCs 处理的任务现在受益于事件驱动以及使用真实的日志进行通信。这些变化带来一个可喜的结果,Kafka 现在可以支持比以前多得多的分区。让我们更详细地讨论一下。
Kafka 集群可以支持的分区数由两个属性决定:每节点分区数限制和集群范围的分区限制。两者都很有趣,但迄今为止,元数据管理一直是集群范围限制的主要瓶颈。尽管还有更多的工作可以做,但以前的 Kafka 改进建议(KIP)只改进每个节点的限制。但是 Kafka 的可伸缩性主要依赖于添加节点来获得更多的容量。这就是集群范围限制变得重要的地方,因为它定义了系统内可伸缩性的上限。
新的仲裁控制器设计目的,是用于处理每个集群中更多的分区。为了评估这一点,我们运行了测试,这个测试与之前在 2018 年运行的测试类似,以公布 Kafka 固有的分区限制。这些测试是对关机和恢复所花费的时间进行测量,这是旧控制器的一个 O(#分区)操作。正是这个操作为 Kafka 现在在单个集群中可以支持的分区数量设置了上限。之前的实现,如 Jun Rao 在上面提到的文章中所解释的,可以实现 200000 个分区,限制因素是在外部元数据服务(ZooKeeper)和内部控制器领导节点(Kafka controller)之间移动关键元数据所花费的时间。使用新的仲裁控制器,这两个角色都由同一个组件提供服务。事件驱动的方法意味着控制器故障转移现在几乎是瞬时的。下面是在我们实验室中执行的运行 200 万个分区(前一个上限的 10 倍)的群集的摘要数字:
控制和非控制停机的措施都很重要。受控关机会影响常见的操作场景,例如滚动重启:部署软件更改的标准过程,同时在整个过程中保持可用性。从不受控制的停机中恢复可以说是更为重要的,因为它设定了系统的恢复时间目标(RTO),例如,在发生意外故障(如 VM 或 pod 崩溃或数据中心变得不可用)之后。虽然这些度量只是更广泛系统性能的指标,但它们直接度量出使用 ZooKeeper 带来的显著瓶颈。注意,受控测量和非受控测量不能直接比较。不受控的关闭案例里包括选举新 leader 所需的时间,而受控案例里不包括。这种差异是为了在受控情况下,与 Jun Rao’s 的原始测量值保持一致。
Kafka 经常被认为是重量级的基础设施,管理 ZooKeeper(第二个独立的分布式系统)的复杂性是这种看法存在的一个重要原因。这通常会导致项目在开始时选择较轻的消息队列,例如 ActiveMQ 或 RabbitMQ 之类的传统队列,并在规模需要时迁移到 Kafka。
这是不适当的,因为 Kafka 提供的抽象,是围绕提交日志形成的,适用于您在初创公司可能看到的小规模工作负载,就像适用于 Netflix 的高通量工作负载一样。此外,如果您想添加流处理,您需要 Kafka 及其提交日志的抽象,无论它是使用 Kafka Streams、ksqlDB 还是其他流处理框架。但由于管理两个独立系统的复杂性(Kafka 和 Zookeeper),用户常常觉得他们必须在规模和易用性之间做出选择。
现在已经不是这样了。KIP-500 和 KRaft 模式提供了一种很好的、轻量级的方法来开始使用 Kafka,或者将其用作 ActiveMQ 或 RabbitMQ 等单一代理的替代品。轻量级的单进程部署也更适合边缘场景和使用轻量级硬件的场景。云为同样的问题增加了一个有趣的切入点。像 Confluent 云这样的托管服务完全消除了操作负担。因此,无论您是希望运行自己的集群,还是让它为您运行,您都可以从小规模开始,随着基础用例的扩展(可能)扩展到大规模,所有这些都使用相同的基础架构。让我们看看单进程部署的情况。
今天新的仲裁控制器在 trunk 中以实验模式提供,预计将包含在即将发布的 Apache kafka2.8 版本中。那你能用它做什么呢?如前所述,一个简单但非常酷的新特性是创建单进程 Kafka 集群的能力,如下面的简短演示所示(省略操作录屏)。当然,如果您想扩展它以支持更高的吞吐量并添加副本以实现容错,那么您只需要添加新的 broker 进程。如您所知,这是基于 KRaft 的仲裁控制器的早期访问版本。请不要将其用于关键工作负载。在接下来的几个月里,我们将添加最后丢失的部分,执行协议的 TLA+建模,并在 Confluent 云中强化仲裁控制器。您现在可以自己试用新的仲裁控制器。查看 GitHub 上的 README。
如果没有 Apache-Kafka 社区和一群分布式系统工程师在流感大流行期间不知疲倦地工作,在大约 9 个月内从零变为一个工作系统,这(并将继续是)项巨大努力将不可能实现。我们要特别感谢 Colin McCabe、Jason Gustafson、Ron Dagostino、Boyang Chen、David Arthur、Jose Garcia Sancio、Guozhang Wang、Alok Nikhil、Deng Zi Ming、Sagar Rao、Feyman、Chia Ping Tsai、Jun Rao、Heidi Howard 以及所有帮助实现这一目标的 Apache Kafka 社区成员。
领取专属 10元无门槛券
私享最新 技术干货