1.概述
最近Kafka官网发布了2.8版本,在该版本中引入了KRaft模式。鉴于新版本和新特性的引入,相关使用资料较少,那边本篇博客笔者将为大家介绍Kafka2.8的安装和使用。
2.内容
2.1 版本介绍
2.1.1 目的
从Kafka2.8版本开始,可以不用Apache Zookeeper来作为Kafka的依赖组件了,官网把这种称之为KRaft模式。目前,Kafka使用Zookeeper来存储有关分区和Broker的元数据,并选择一个Broker作为Kafka的Controller。现在官网打算删除对Zookeeper的依赖,让Kafka能够以更具扩展性和更加强大的方式管理元数据,从而支持更多分区。
2.1.2 元数据作为事件日志
我们经常会讨论将状态作为一系列事件进行管理的好处。单个数字(偏移量)描述了Consumer在Stream中的位置。只需要重播所有比其当前偏移量新的事件,多个Consumer就可以迅速赶上最新的状态。该日志在事件之间建立了清晰的顺序,并确保使用者始终沿着单个时间轴移动。
但是,这些好处Kafka却没有享受到,对元数据的更改视为互相之间没有关系的孤立更改。当Controller向集群中的其他Broker推送状态更改通知(例如LeaderAndIsrRequest)时,Broker可能会获取部分更改,但不是全部。尽管Controller重试了几次,但最终还是放弃了。这样会使Broker处理分歧状态,更坏的是,尽管Zookeeper是记录的存储,但是Zookeeper中的状态通常与Controller内存中保存的状态不匹配。例如,当分区Leader在Zookeeper中更改了其ISR时,Controller通常在几秒钟内不了解这些更改。Controller没有通用的方法来遵循Zookeeper事件日志。尽管Controller可以设置一次Watch,但是由于性能原因,Watch的数量受到限制。监视触发时,它不会告诉Controller当前状态,而只是告诉状态已更改。到Controller重新读取znode并设置新的Watch时,状态可能已经与Watch最初触发时的状态有所变化。如果没有Watch,则Controller可能根本不了解该变化。
某些情况下,元数据应该存储在Kafka中,而不是存储在单独的系统中。这将避免与Controller状态和Zookeeper状态之间的差异相关的所有问题。Broker不应将通知发送给Broker,而应仅使用事件日志中的元数据事件。这样可以确保元数据更改始终以相同的顺序到达。Broker将能够在文件中本地存储元数据。当它们启动时,它们只需要从Controller读取已更改的内容,而无需读取完整状态。这将使我们以更少的CPU消耗支持更多的分区。
2.1.2 简化部署和配置
Zookeeper是一个单独的系统,具有自己的配置文件语法,管理工具和部署模式。这意味着系统管理员需要学习如何管理和部署两个独立的分布式系统才能部署Kafka。对于管理员来说,这可能是一项艰巨的任务,特别是如果他们对部署Java服务不是很熟悉的话。统一该系统将大大改善运行Kafka的首次体验,并有助于扩大其应用范围。
由于Kafka和Zookeeper配置是分开的,因此很容易出错。例如,管理员可能在Kafka上设置了SASL,并且错误的认为它们已经保护了通过网络传输的所有数据。实际上,这样做还必须在单独的在Zookeeper系统中部署配置安全性。
2.2 架构介绍
KIP-500提出了可扩展的Zookeeper Kafka后的总体构想,为了展示整体情况,忽略了诸如RPC格式、磁盘格式等详细信息。如下图所示:
当前,Kafka集群包含了几个Broker节点,以及Zookeeper节点的外部选举。在此图中,我们描绘了4个Broker节点和3个Zookeeper节点。这是小型集群的典型规划。Controller(用白色表示)在选定后从Zookeeper Leader中加载其状态。从Controller延伸到Broker中其他节点的箭头指向表示Controller推送的更新,例如LeadAndIsr和UpdateMetaData消息。
需要注意的是,除了Controller之外的其他Broker可以并且确实与Zookeeper能够进行通信。因此,实际上,应该从每个Broker到Zookeeper进行划清界限。另外,外部命令行工具和应用程序可以在Zookeeper中修改状态,而无需Controller的参与。如前所述,这些问题使得很难知道Controller上的内存中的状态是否真正反映了Zookeeper中的持久状态。
在提出的体系结构中,三个Controller节点替代了三个Zookeeper节点。Controller节点和Broker节点在单独的JVM中运行。Controller节点为元数据分区选择单个Leader,以白色表示。Broker不是从Controller向Broker发布更新,而是从该Leader提取元数据更新。这就是为什么箭头指向Controller而不是Broker的原因。
请注意,尽管Controller进程在逻辑上与Broker进程是分开的,但是它们在物理上并不需要分开。在某些情况下,将某些或者所有Controller进程与Broker进程部署在同一节点上可能是有意义的。这类似于Zookeeper进程可以与较小规模的集群中的Kafka Broker部署在相同的节点上。
2.3 Controller数量
Controller节点包括一个Raft选举,用于管理元数据日志。该日志包含有关集群元数据的每次更改的信息。当前存储在Zookeeper中的所有内容,例如Topic,分区,ISR,配置等,都将存储在此日志中。
使用Raft算法,Controller节点将在不依赖任何外部系统的情况下从他们当中选出一个Leader。元数据日志的Leader称之为Active Controller。Active Controller处理由Broker生成的所有RPC,Follower Controller复制写入数据到Active Controller,并在Active Controller出现故障时用作热备用服务器(HA)。由于Controller现在都将跟踪最新状态,因此Controller故障转移将不需要很长的重新加载时间,在此期间我们将所有状态都转移到新的Controller。
就像Zookeeper一样,Raft需要大多数节点才能继续运行。因此,三个节点Controller集群可以承受一次故障。五个节点Controller集群可以承受两次故障,依此类推。Controller定期将元数据的快照写到磁盘上,尽管从概念上讲这与压缩类似,但是代码路径会有所不同,因为我们可以简单的从内存中读取状态,而不是从磁盘中重新读取日志。
2.4 Broker元数据管理
这些Broker将通过新的MetadataFetch API从Active Controller中获取更新,而不是Controller将更新推送给其他Broker。MetadataFetch与fetch请求类似,就像fetch请求一样,Broker将跟踪其获取的最后更新的偏移量(offset),并且仅从Active Controller中请求更新。
Broker将把提取到磁盘的元数据持久化。即使存在成千上万甚至数百万个分区,这也可以使Broker快速启动。(请注意,由于这种持久性是一种优化,因此如果使开发更容易,可以将其保存在第一个版本之外)。
在大多数情况下,Broker只需要获取增量,而无需获取完整状态。但是,如果Broker与Active Controller的距离太远(落后Active Controller太远),或者Broker完全没有缓存的元数据,则Controller将发送完整的元数据镜像,而不是一系列增量。
Broker将定期向Active Controller请求元数据更新,该请求作为心跳,让Controller知道Broker处理活跃状态。
2.5 Broker状态机制
目前,Broker在启动后立即向Zookeeper注册,该注册完整了两件事:它使Broker知道了它是否已经被选举为Controller,并且它使其他节点知道如何联系它。
当前,如果Broker丢失了其Zookeeper会话,则Controller会将其从集群元数据中删除。在以后Zookeeper时代,如果Active Controller在足够长的时间内未发送MetadataFetch心跳,则Active Controller将从集群元数据中删除Broker。
在当前情况下,可以联系Zookeeper从Controller进行分区的Broker将继续满足用户的请求,但是不会接收任何元数据更新。这可能会导致一些令人困惑和困难的情况。例如,使用acks=1的生产者可能会继续生产实际上不再是该Leader的Leader,但是无法接收到Controller的LeaderAndIsrRequest来迁移Leader。
在以后Zookeeper的世界中,集群成员身份与元数据更新集成在一起,如果Broker无法接收元数据更新,则他们将无法继续成为集群的成员。尽管仍然可以从特定Client对Broker进行分区,但是如果从Controller进行了分区,则Broker将从集群中删除。
- Offline:当Broker进程处理脱机状态时,它要么根本不运行,要么不执行启动所需要的单节点任务,例如初始化JVM或执行日志恢复;
- Fenced:当Broker处理Fenced状态时,它将不会响应来自Client的RPC。当启动并尝试获取最新的元数据时,Broker将处理受保护状态。如果无法联系Active Controller,它将重新进入隔离状态。发送给Client的元数据中应省略受保护的Broker;
- Online:当Broker在线时,它准备响应Client的请求;
- Stopping:Broker收到SIGINT时便进入停止状态,这表明系统管理员要关闭Broker。当Broker停止时,它仍在运行,但是我们正在尝试将分区Leader移除该Broker。最终,Active Controller将通过MetadataFetchResponse中返回特殊的结果代码来要求Broker最终脱机。或者,如果Leader不能在预定时间内移动,则Broker将关闭。
另外,以前直接写给Zookeeper的需要操作将变为Controller操作。例如,更改配置,更改默认授权者存储的ACL等。Client的新版本应将这些操作直接发送到Active Controller,它将与新旧集群一起使用。为了保持与将这些操作发送给随机Broker的旧Client的兼容性,Broker会将这些请求转发到Active Controller。
在某些情况下,需要创建一个新的API来替换以前通过Zookeeper完成的操作,这样的一个示例是,当分区的管理员想要修改同步副本集时,它当前直接就该Zookeeper。在以后Zookeeper中,管理员将改为向Active Controller进行RPC。
当前,某些工具和脚本直接与Zookeeper联系,在以后的Zookeeper中,这些工具必须改用为Kafka API。
3.Kafka2.8
KRaft目前在Kafka2.8版本是一个测试版本,KRaft模式不推荐使用到生产环境。当Kafka集群处理KRaft模式时,它不会将其元数据存储在Zookeeper中,实际上根本不需要运行Zookeeper,因为它将元数据存储在Controller节点中。
目前官网退出的KRaft模式仅用于测试,不推荐使用到生产环境,因为官方还不支持将现有的基于Zookeeper的Kafka集群升级到KRaft模式。实际上,当Kafka3.0发布时,无法将Kafka集群从2.8升级到3.0,目前该模式会有些BUG,如果尝试KRaft使用到生产环境,会存在数据丢失的风险。
3.1 使用步骤
步骤1:生成集群ID
先生成集群ID,需要使用kafka-storage工具,命令如下:
./bin/kafka-storage.sh random-uuid
步骤2:格式化存储目录
接下来的步骤是格式化存储目录,如果你是运行单节点模式,你可以执行如下命令:
# wzhDv517Siy99Rm42vTHEA 这个是执行上面的命令获取到的集群ID ./kafka-storage.sh format -t wzhDv517Siy99Rm42vTHEA -c ../config/kraft/server.properties
如果,你使用的多节点安装,你需要在每个节点上执行格式化命令,并确保每个节点使用的集群ID是相同的。
步骤3:启动Kafka服务
最后,你可以在每个节点上启动Kafka服务,命令如下:
./kafka-server-start.sh ../config/kraft/server.properties
之后,我们可以使用jps命令查看Kafka进程是否已经成功启动。
在启动Kafka进程后,我们可以和之前Zookeeper+Kafka的模式一样,通过连接9092端口来操作Topic,Consumer和Producer。例如,创建一个分区为1,副本1的Topic,命令如下:
./kafka-topics.sh --create --topic ke28 --partitions 1 --replication-factor 1 --bootstrap-server localhost:9092
3.2 Controller Server
在KRaft模式下,只有一小部分特别选定的服务器可以充当Controller(与基于Zookeeper的模式不同,在这种模式下,任何服务器都可以成为Controller)。特别选定的Controller服务器将参与元数据的管理,每个Controller服务器要么是Active,要么是Standby。
我们通常会为此角色选择3台或者5台服务器,具体取决于成本和系统应承受的并发故障数等因素。就像Zookeeper一样,为了保持可用性,必须保持大多数Controller处理在线状态,如果你有3个Controller,可以容忍1次故障,使用5个Controller,可以容忍2次故障。
3.3 Process Roles
每个Kafka服务现在有了一个新的配置,它被称为“process.roles”,它有如下可选值:
- broker:它在KRaft模式中扮演一个Broker角色;
- controller:它在KRaft模式中扮演一个Controller角色;
- broker,controller:它在KRaft模式中同时扮演Broker和Controller角色;
- 如果没有设置,则假定我们处于ZooKeeper模式。如前所述,如果不重新格式化,当前无法在ZooKeeper模式和KRaft模式之间来回转换。
充当Broker和Controller的节点称为“combined”节点,该节点对简单用例操作更简单,并且允许避免与JVM相关的一些固定内存开销。关键的缺点是Controller与系统的其余部分隔离较少。例如,如果Broker上发生OOM,服务器的Controller部分不会与该OOM隔离。
3.4 Quorum Voters
系统中的所有节点都必须设置“controller.quorum.votters”配置,这标识应使用的是选举Controller的服务器,必须枚举所有的Controller。这类似在使用Zookeeper时,“zookeeper.connect”配置必须包含所有的Zookeeper服务器。但是,与Zookeeper配置不同的是,“controller.quorum.configures”还有每个节点ID,格式为id1@host1:port1,id2@host:port2等。
所以,如果你有10个Broker和3和Controller命名为controller1,controller2,controller3,你可以按照如下进行配置:
process.roles=controller node.id=1 listeners=CONTROLLER://controller1.dn1.kafka-eagle.org:9093 controller.quorum.voters=1@controller1.dn1.kafka-eagle.org:9093,2@controller2.dn2.kafka-eagle.org:9093,3@controller3.dn3.kafka-eagle.org:9093
每个Broker和Controller必须设置“controller.quorum.votters”。请注意,“controller.quorum.votters”配置中提供的节点ID必须与提供给服务器的节点ID匹配。
所以在Controller1上,node.id必须设置为1,依此类推。这里不要求Controller的ID从0或者1开始。然而,最简单和最容易混淆的分配方法节点ID可能只是给每个服务器一个数字ID,从0开始。
需要注意的是,Client端不需要配置“controller.quorum.votters”,只有服务器端才需要配置。
3.5 kafka-dump-log
这里我们可以通过kafka-dump-log.sh工具来查看metadata日志信息,命令如下所示:
./kafka-dump-log.sh --cluster-metadata-decoder --skip-record-metadata --files /data/soft/new/kafka2.8/data/kraft/@metadata-0/*.log
执行结果如下图所示:
3.5 Metadata Shell
可以通过kafka-metadata-shell.sh来查看元数据信息,这个和Zookeeper Client操作很类似,命令如下:
./kafka-metadata-shell.sh --snapshot /data/soft/new/kafka2.8/data/kraft/@metadata-0/00000000000000000000.log
然后,进入到一个命令行操作界面,操作步骤如下所示:
4.总结
目前Kafka2.8中的KRaft处于测试阶段,大家如果是学习可以尝试安装部署了解和认识这个KRaft模式,生产环境暂时建议不推荐使用。Kafka2.8的主线版本还是以依赖Zookeeper为主,博主开发的Kafka-Eagle监控工具仍然可以正常监控Kafka2.8,当然KRaft模式的兼容,博主在Kafka-Eagle的测试分支中在进行开发,当Kafka官网将KRaft切换为主线版本时,Kafka-Eagle也会及时切换KRaft作为主线版本来监控Kafka。最后,欢迎大家使用Kafka-Eagle监控工具。
5.结束语
这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!
另外,博主出书了《Kafka并不难学》和《Hadoop大数据挖掘从入门到进阶实战》,喜欢的朋友或同学, 可以在公告栏那里点击购买链接购买博主的书进行学习,在此感谢大家的支持。关注下面公众号,根据提示,可免费获取书籍的教学视频。