引自: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)模式。
请注意,有些功能在此早期版本中不可用。我们还不支持使用ACL和其他安全功能或事务。另外,在KRaft模式下,分区重新分配和JBOD都不予支持(预计今年晚些时候apache kafka版本中会提供这些功能)。因此,采用基于Raft共识协议的仲裁控制器机制,我们不建议将它用于生产工作环境。不过,如果你真的试用了这个软件,你会发现许多新的优点:部署和操作更简单,你可以将Kafka作为一个进程整体运行,它可以为每个集群容纳更多的分区(参见下面的度量)。
仲裁控制器:事件驱动的共识
如果选择使用新的仲裁控制器运行Kafka,则以前由Kafka控制器和ZooKeeper承担的所有元数据职责都将合并到这个新服务中,在Kafka集群内部运行。如果用例中有需要,仲裁控制器也可以运行在专有硬件上。
然而,从内部来看,这很有趣。仲裁控制器使用新的KRaft协议,确保在仲裁中准确地复制元数据。这个协议在许多方面与ZooKeeper的ZAB协议和Raft协议相似,但是有一些重要的区别,一个值得注意的区别是它使用了事件驱动架构。
仲裁控制器使用事件源存储模型存储其状态,这可确保内部状态机始终可以被准确地重建。事件日志用于存储此状态(也称为元数据主题),会被快照定期删节,以确保日志不会无限增长。仲裁中的其他控制器通过以下方式来同步活动控制器:响应日志中的创建和存储事件。因此,如果一个节点由于分区事件而暂停,例如,当它重新加入时,它可以通过访问日志来快速赶上它错过的任何事件。这大大减少了服务不可用窗口,改善了系统最坏情况下的恢复时间。
KRaft协议的事件驱动特性意味着,与基于ZooKeeper的控制器不同,仲裁控制器不需要从ZooKeeper加载状态来变成活动状态。当leader节点发生变化时,新的活动控制器已经在内存中拥有所有提交的元数据记录。此外,KRaft协议中使用的事件驱动机制也用于跨集群跟踪元数据。以前用RPCs处理的任务现在受益于事件驱动以及使用真实的日志进行通信。这些变化带来一个可喜的结果,Kafka现在可以支持比以前多得多的分区。让我们更详细地讨论一下。
扩展Kafka:支持数百万个分区
| Kafka集群可以支持的分区数由两个属性决定:每节点分区数限制和集群范围的分区限制。两者都很有趣,但迄今为止,元数据管理一直是集群范围限制的主要瓶颈。尽管还有更多的工作可以做,但以前的Kafka改进建议(KIP)只改进每个节点的限制。但是Kafka的可伸缩性主要依赖于添加节点来获得更多的容量。这就是集群范围限制变得重要的地方,因为它定义了系统内可伸缩性的上限。 新的仲裁控制器设计目的,是用于处理每个集群中更多的分区。为了评估这一点,我们运行了测试,这个测试与之前在2018年运行的测试类似,以公布Kafka固有的分区限制。这些测试是对关机和恢复所花费的时间进行测量,这是旧控制器的一个O(#分区)操作。正是这个操作为Kafka现在在单个集群中可以支持的分区数量设置了上限。之前的实现,如Jun Rao在上面提到的文章中所解释的,可以实现200000个分区,限制因素是在外部元数据服务(ZooKeeper)和内部控制器领导节点(Kafka controller)之间移动关键元数据所花费的时间。使用新的仲裁控制器,这两个角色都由同一个组件提供服务。事件驱动的方法意味着控制器故障转移现在几乎是瞬时的。下面是在我们实验室中执行的运行200万个分区(前一个上限的10倍)的群集的摘要数字: | Operation | With Quorum Controller | With ZooKeeper-Based Controller | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|------------------------|---------------------------------| | Controlled Shutdown Time (2 million partitions) | 32 sec. | 135 sec. | | Recovery from Uncontrolled Shutdown (2 million partitions) | 37 sec. | 503 sec. |
控制和非控制停机的措施都很重要。受控关机会影响常见的操作场景,例如滚动重启:部署软件更改的标准过程,同时在整个过程中保持可用性。从不受控制的停机中恢复可以说是更为重要的,因为它设定了系统的恢复时间目标(RTO),例如,在发生意外故障(如VM或pod崩溃或数据中心变得不可用)之后。虽然这些度量只是更广泛系统性能的指标,但它们直接度量出使用ZooKeeper带来的显著瓶颈。注意,受控测量和非受控测量不能直接比较。不受控的关闭案例里包括选举新leader所需的时间,而受控案例里不包括。这种差异是为了在受控情况下,与Jun Rao's的原始测量值保持一致。
缩减 Kafka:将 Kafka 作为单个进程
Kafka经常被认为是重量级的基础设施,管理ZooKeeper(第二个独立的分布式系统)的复杂性是这种看法存在的一个重要原因。这通常会导致项目在开始时选择较轻的消息队列,例如ActiveMQ或RabbitMQ之类的传统队列,并在规模需要时迁移到Kafka。
这是不适当的,因为Kafka提供的抽象,是围绕提交日志形成的,适用于您在初创公司可能看到的小规模工作负载,就像适用于Netflix的高通量工作负载一样。此外,如果您想添加流处理,您需要Kafka及其提交日志的抽象,无论它是使用Kafka Streams、ksqlDB还是其他流处理框架。但由于管理两个独立系统的复杂性(Kafka和Zookeeper),用户常常觉得他们必须在规模和易用性之间做出选择。
现在已经不是这样了。KIP-500和KRaft模式提供了一种很好的、轻量级的方法来开始使用Kafka,或者将其用作ActiveMQ或RabbitMQ等单一代理的替代品。轻量级的单进程部署也更适合边缘场景和使用轻量级硬件的场景。云为同样的问题增加了一个有趣的切入点。像Confluent 云这样的托管服务完全消除了操作负担。因此,无论您是希望运行自己的集群,还是让它为您运行,您都可以从小规模开始,随着基础用例的扩展(可能)扩展到大规模,所有这些都使用相同的基础架构。让我们看看单进程部署的情况。
带你体验无zk的Kafka
今天新的仲裁控制器在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社区成员。