1、简介
Kafka是一个能够支持高并发以及流式消息处理的消息中间件,并且Kafka天生就是支持集群的,今天就主要来介绍一下如何搭建Kafka集群。
Kafka目前支持使用Zookeeper模式搭建集群以及KRaft模式(即无Zookeeper)模式这两种模式搭建集群,这两种模式各有各的好处,今天就来分别介绍一下这两种方式
1.1、Kafka集群中的节点类型
一个Kafka集群是由下列几种类型的节点构成的,它们充当着不同的作用:
Broker节点
:即代理节点,是Kafka中的工作节点,充当消息队列的角色,负责储存和处理消息,每个Broker都是一个独立的Kafka服务器,可以在不同的机器上运行,除此之外Broker还负责分区(partition)的管理,将主题(topic)划分为多个分区,并分布在集群的不同Broker上Controller节点
:即控制器节点,是集群中的特殊节点,负责储存和管理整个集群元数据和状态,它能够监控整个集群中的Broker,在需要时还能够进行平衡操作混合节点
:即同时担任Broker和Controller节点角色的节点
1.2、两重模式的搭建方式
- Zookeeper模式集群
- KRaft模式集群
2、Zookeeper模式集群
这是一种比较简单,相对“传统”的搭建方式了!在这种模式下,每个Kafka节点都是依赖于Zookeeper的,使用Zookeeper存储集群中所有节点的元数据。
只要所有的Kafka节点连接到同一个Zookeeper上面(或者同一个Zookeeper集群),这些Kafka节点就构成了一个集群。所以说就算是只有一个Kafka节点在运行,这一个节点也可以称作一个集群。
在Zookeeper模式集群中,Zookeeper节点(或者集群)就充当了Controller的角色,而所有的Kafka节点就充当着Broker的角色。
下面就来介绍一下搭建过程,这里在1台主机上分别运行Zookeeper和Kafka来模拟一个集群,一共一个Zookeeper节点和三个Kafka节点构成,如下:
节点名 | 地址和端口 |
---|---|
Zookeeper节点 | localhost:2181 |
Kafka节点1 | localhost:9092 |
Kafka节点1 | localhost:9093 |
Kafka节点1 | localhost:9094 |
1、搭建Zookeeper
首先我们要运行起一个Zookeeper节点,这里就不再赘述Zookeeper节点如何搭建了,搭建可以查看官方文档,或者使用Docker的方式搭建。
搭建完成并运行Zookeeper之后,我们会把所有的Kafka节点都配置到这一个Zookeeper节点上。
2、配置并运行所有Kafka节点
修改每台虚拟机的Kafka目录中的配置文件,配置文件位于解压的Kafka文件夹中的config/server.properties,对应的三个配置文件如下:
server1.properties
# 每个节点的id,不要不相同的整数
broker.id=1
# 存储位置
log.dirs=/tmp/kafka-logs/cluster/log1
# zookeeper集群的话,多个地址用逗号分割
zookeeper.connect=localhost:2181
# zookeeper连接超时时间
zookeeper.connection.timeout.ms=18000
# 外网暴露
advertised.listeners=PLAINTEXT://localhost:9092
# 内网端口
listeners=PLAINTEXT://localhost:9092
server2.properties
# 每个节点的id,不要不相同的整数
broker.id=2
# 存储位置
log.dirs=/tmp/kafka-logs/cluster/log2
# zookeeper集群的话,多个地址用逗号分割
zookeeper.connect=localhost:2181
# zookeeper连接超时时间
zookeeper.connection.timeout.ms=18000
# 外网暴露
advertised.listeners=PLAINTEXT://localhost:9093
# 内网端口
listeners=PLAINTEXT://localhost:9093
server3.properties
# 每个节点的id,不要不相同的整数
broker.id=3
# 存储位置
log.dirs=/tmp/kafka-logs/cluster/log3
# zookeeper集群的话,多个地址用逗号分割
zookeeper.connect=localhost:2181
# zookeeper连接超时时间
zookeeper.connection.timeout.ms=18000
# 外网暴露
advertised.listeners=PLAINTEXT://localhost:9094
# 内网端口
listeners=PLAINTEXT://localhost:9094
三台虚拟机配置完成后,分别使用终端进入到Kafka目录下并启动,执行下列命令:
./kafka-server-start.sh -daemon ../config/cluster/server1.properties
./kafka-server-start.sh -daemon ../config/cluster/server2.properties
./kafka-server-start.sh -daemon ../config/cluster/server3.properties
到此,整个集群就搭建完成了。
3、测试
先在kafka1的端口上面再开一个终端并进入Kafka目录,执行下列命令创建cluster-topic,共3个分区,每个人去都分配3个副本:
./kafka-topics.sh --bootstrap-server localhost:9092 --create --topic cluster-topic --partitions 3 --replication-factor 3
然后在kafka2的端口查询该topic:
./kafka-topics.sh --bootstrap-server localhost:9093 --describe --topic cluster-topic
显而易见,该topic下有3个分区,每个分区有三个副本。每个分区的leader分别是1,2,3,表明kafka将该topic的3分区均匀地在3台broker上进行了分配。
删除分区:
./kafka-topics.sh --bootstrap-server localhost:9093 --delete --topic cluster-topic
3、KRaft模式集群
在上述传统方案中,Kafka需要依赖Zookeeper完成元数据存放和共享,这样也就暴露出了一些问题:
- 搭建Kafka集群时还需要额外搭建Zookeeper,增加了运维成本
- Zookeeper是强一致性的组件(符合CP理论),如果集群中数据发生变化,那么必须要等到其它节点都同步,至少超过一半同步完成,这样节点数多性能差
KRaft模式是新版本Kafka中推出的集群模式,这种模式下就完全不需要Zookeeper了!只需要数个Kafka节点就可以直接构成集群,在这时集群中的Kafka节点既有可能是Controller节点也可能是Broker节点,在这个模式中,我们不仅可以手动配置某个节点的角色(是Controller还是Broker),还可以使其同时担任Broker和Controller角色(混合节点)。
在KRaft模式中,集群的节点会通过投票选举的方式,选择出一个主要的Controller节点,这个节点也称作领导者,它将负责维护整个集群的元数据和状态信息,那么其它的Controller节点或者混合节点就称之为追随者,它们会从领导者同步集群元数据和状态信息。如果领导者宕机了,所有的节点会重新投票选举一个新的领导者。
在选举过程中,所有的节点都会参与投票过程,而候选节点只会是Controller节点或者混合节点(即Broker节点不会被选举为领导者)。
需要注意的是,在默认情况下Kafka集群中的Broker节点和Controller节点通常会监听不同的端口:
- Broker节点是Kafka集群中的数据节点(消息队列),它们负责接收客户端的消息和传递消息给客户端,默认情况下,每个Broker节点会监听9092端口,该端口用于与客户端进行通信,客户端可以将消息发送到这个端口,或者从这个端口接收消息,这个端口可以称作客户端通信端口
- Controller节点是Kafka集群中的控制器节点,负责管理集群的状态和元数据,Controller节点监听的端口通常是9093,该端口用于集群中其他节点获取元数据或在混合节点选举新的Controller时进行通信,通过该端口,其他节点可以与Controller节点交互,获取集群的元数据信息或参与控制器的选举过程,这个端口可以称作控制器端口
- 混合节点(即同时担任Broker和Controller角色的节点)中,这两个端口都会被使用,默认情况下混合节点将监听9092端口接收和传递消息给客户端,并监听9093端口用于与其他节点进行元数据交换和控制器选举通信,可见混合节点会同时使用两个端口分别作为客户端通信端口与控制器端口
所以需要根据实际情况配置网络设置和防火墙规则,以确保Kafka集群中的节点能够在正确的端口上进行通信。上述提到的两种端口也是可以修改的,当然不建议修改。
同样地,就算是你只是搭建了一个Kafka节点,这一个节点也仍然被视为一个Kafka集群,并且KRaft模式下如果只需要建立一个节点,那么这个节点必须是混合节点。
单台主机模拟搭建三个Kafka节点构成的KRaft模式集群如下:
节点名 | 地址 | 节点类型 | 客户端通信端口 | 控制器端口 |
---|---|---|---|---|
Kafka节点1 | localhost | 混合节点 | 9092 | 9093 |
Kafka节点2 | localhost | 混合节点 | 9094 | 9095 |
Kafka节点3 | localhost | 混合节点 | 9096 | 9097 |
1、修改配置文件
在KRaft模式下,配置文件位于Kafka目录中的config/kraft/server.properties,使用文本编辑器打开并找到下列配置以修改:
node.id
表示这个节点的id,一个集群中每个节点id不能重复,需要是不小于1的整数,这里三台虚拟机的配置分别为1,2和3(类似上述Zookeeper的broker.id配置)controller.quorum.voters
设定投票者列表,即需要配置所有的Controller节点id及其地址端口,配置格式为节点1的id@节点1地址:节点1端口,节点2的id@节点2地址:节点2端口,节点3的id@节点3地址:节点3端口…,这里的端口需要是控制器端口,默认都是9093,上面也提到过了,默认不需要修改advertised.listeners
表示这个Kafka节点的外网地址,这里分别配置为PLAINTEXT://localhost:9092,PLAINTEXT://localhost:9094和PLAINTEXT://localhost:9096(和上述Zookeeper模式中的一样,实际在服务器上搭建时替换为服务器的外网地址或者域名)- process.roles 表示设定这个节点的类型,设定为broker表示设定这个节点为Broker节点,同样地设定controller表示设定为Controller节点,默认是broker,controller表示这个节点会自动切换节点类型
server1.properties
# 节点类型,默认为混合节点
process.roles=broker,controller
# 节点id,为不小于1的整数
node.id=1
# 投票者列表:nodeId+地址端口
controller.quorum.voters=1@localhost:9093,2@localhost:9095,3@localhost:9097
# 内网地址
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
inter.broker.listener.name=PLAINTEXT
# 外网地址
advertised.listeners=PLAINTEXT://localhost:9092
controller.listener.names=CONTROLLER
log.dirs=/tmp/kraft-combined-logs/log1
server2.properties
# 节点类型,默认为混合节点
process.roles=broker,controller
# 节点id,为不小于1的整数
node.id=2
# 投票者列表:nodeId+地址端口
controller.quorum.voters=1@localhost:9093,2@localhost:9095,3@localhost:9097
# 内网地址
listeners=PLAINTEXT://:9094,CONTROLLER://:9095
inter.broker.listener.name=PLAINTEXT
# 外网地址
advertised.listeners=PLAINTEXT://localhost:9094
controller.listener.names=CONTROLLER
log.dirs=/tmp/kraft-combined-logs/log2
server3.properties
# 节点类型,默认为混合节点
process.roles=broker,controller
# 节点id,为不小于1的整数
node.id=3
# 投票者列表:nodeId+地址端口
controller.quorum.voters=1@localhost:9093,2@localhost:9095,3@localhost:9097
# 内网地址
listeners=PLAINTEXT://:9096,CONTROLLER://:9097
inter.broker.listener.name=PLAINTEXT
# 外网地址
advertised.listeners=PLAINTEXT://localhost:9096
controller.listener.names=CONTROLLER
log.dirs=/tmp/kraft-combined-logs/log3
2、生成集群ID并使用集群ID格式化数据目录
在KRaft模式下,一个集群需要设定一个id,我们可以使用自带的命令生成,先进入上述任意一台虚拟机并使用终端进入Kafka目录中,执行下列命令生成一个UUID:
bin/kafka-storage.sh random-uuid
这里记录下这个ID以备用。kilMbKEoRUq3Ha6nruW6Aw
这个集群ID事实上是一个长度16位的字符串通过Base64编码后得来的,因此你也可以不使用上述命令,直接自定义一个16位长度的纯英文和数字组成的字符串,然后将这个字符串编码为Base64格式作为这个集群ID也可以。可以使用菜鸟工具中的在线Base64编码工具。
然后,分别执行下列命令,配置集群元数据:
kafka-storage.sh format -t 生成的集群ID -c ../config/kraft/cluster/server1.properties
kafka-storage.sh format -t kilMbKEoRUq3Ha6nruW6Aw -c ../config/kraft/cluster/server2.properties
kafka-storage.sh format -t kilMbKEoRUq3Ha6nruW6Aw -c ../config/kraft/cluster/server3.properties
3、启动Kafka集群
使用终端,分别执行下列命令:
kafka-server-start.sh -daemon ../config/kraft/cluster/server1.properties
kafka-server-start.sh -daemon ../config/kraft/cluster/server2.properties
kafka-server-start.sh -daemon ../config/kraft/cluster/server3.properties
4、创建topic测试
先在kafka1的端口(9092)上面再开一个终端并进入Kafka目录,执行下列命令创建cluster-topic,共3个分区,每个人去都分配3个副本:
./kafka-topics.sh --bootstrap-server localhost:9092 --create --topic cluster-topic --partitions 3 --replication-factor 3
然后在kafka2的端口(9094)查询该topic:
./kafka-topics.sh --bootstrap-server localhost:9094 --describe --topic cluster-topic
在kafka3的端口(9096)删除分区:
./kafka-topics.sh --bootstrap-server localhost:9096 --delete --topic cluster-topic
可见集群节点之间可以互相通信。
4、重要配置介绍
4.1、listeners
这个配置项用于指定Kafka服务器监听客户端连接的地址和端口,当 Kafka 服务器启动时,它将监听listeners配置项中指定的地址和端口,等待客户端的连接请求。
一般情况下这个配置以PLAINTEXT://
或者CONTROLLER://
开头,意义如下:
- 若这个节点是Broker节点,则以PLAINTEXT://开头
- 若这个节点是Controller节点,则以CONTROLLER://开头
- 若这个节点是混合节点,则需要同时配置两者开头的地址
这个配置项通常不需要修改,下面给出几个配置示例:
PLAINTEXT://:9092
本节点作为Broker节点,监听本机所有可用网卡的9092端口(使用9092端口作为客户端通信端口),也是默认配置PLAINTEXT://127.0.0.1:9092
本节点作为Broker节点,监听本地的9092端口,这样仅接受来自本地的请求CONTROLLER://:10000
本节点作为Controller节点,监听本机所有可用网卡的10000端口(使用10000端口作为控制器端口)PLAINTEXT://:9092,CONTROLLER://:9093
本节点作为混合节点,监听本机所有可用网卡的9092和9093端口,其中9092作为客户端通信端口,9093作为控制器端口
4.2、advertise.listeners
这个配置容易和listeners混淆,事实上它们是有较大的区别的。
该配置项指定Kafka服务器广播给客户端的地址和端口,通常配置为Kafka所在服务器的外网地址。
当客户端(生产者或消费者)尝试连接到Kafka服务器时,它首先会获取Kafka服务器广播的地址和端口,也就是advertise.listeners配置所指定的地址和端口,然后才会使用advertise.listeners配置所指定的地址和端口来建立与Kafka服务器的连接。
相信这时大家会有个疑问:既然客户端要连接Kafka(例如Spring Boot集成Kafka客户端),那一定是已经知道了Kafka对外的地址端口了,那为什么连接的时候还需要获取一下广播的地址端口再进行连接呢?这样是不是有一些多此一举?
事实上,Kafka设计这个配置是为了解决下面较为复杂的网络场景:
多网络接口的主机部署
:在一个多网络接口的主机部署Kafka时,Kafka服务器可能会监听多个地址和端口,这些地址和端口可能与客户端实际访问的地址和端口不同,advertise.listeners允许服务器指定一个公开的、可访问的地址和端口,以便客户端能够正确连接NAT/代理环境
:在某些网络环境下,Kafka服务器位于一个私有网络中,客户端位于一个公共网络中,两者之间可能存在网络地址转换(NAT)或代理,在这种情况下,Kafka服务器的内部地址和端口对客户端来说是不可访问的。通过使用advertise.listeners,Kafka服务器可以将一个公共地址和端口广播给客户端,使得客户端能够通过公共网络连接到服务器容器环境
:例如你把Kafka放在Docker容器中运行,按照默认配置,Kafka服务端只会监听容器网络的9092端口,我们知道外部不能直接访问容器的网络,而是需要使用网络映射,假设你把Kafka容器的9092端口映射至了宿主机9095端口,也就是说外部需要通过9095端口访问到Kafka容器的9092端口,那么你就配置advertise.listeners为PLAINTEXT://服务器外网地址:9095,客户端就可以正确访问容器中的Kafka了
总之,这个配置设置为Kafka服务器所在的外网地址即可!例如PLAINTEXT://69.54.112.239:9092。
4.3、process.roles
这是KRaft模式下专门的配置,用于配置这个节点的类型,可以配置为下列值:
broker
表示这个节点是Broker节点,充当消息队列的角色controller
表示这个节点是Controller节点,充当元数据存放和管理的角色broker,controller
表示这个节点同时担任Broker和Controller的角色,也称作混合节点
如果没有配置这个选项,则Kafka会以Zookeeper模式运行。
这里有下列注意事项:
-
如果设定节点为controller:
- 则不能配置advertised.listeners,可以将其注释掉或者删掉
- listeners需要配置为CONTROLLER://开头,建议配置为CONTROLLER://:9093
-
如果设定节点为broker:
- 则需要配置advertised.listeners为服务器外网地址和端口,这和Zookeeper模式中相同
- listeners需要配置为PLAINTEXT://开头,建议配置为PLAINTEXT://:9092
-
如果设定节点为混合节点:
- 同样需要配置advertised.listeners为服务器外网地址和端口
- listeners需要同时配置CONTROLLER://和PLAINTEXT://,建议配置为PLAINTEXT://:9092,CONTROLLER://:9093
在开发环境或者小规模集群,可以全部使用混合节点,如果是生产环境就建议设定好每个节点的类型了!并且通常需要先启动Controller节点再启动Broker节点。
事实上,我们发现Kafka的KRaft配置目录config/kraft下有三个配置文件,其中server.properties是混合节点的配置模板,而broker.properties和controller.properties分别是Broker节点和Controller节点的配置模板,大家如果要设定节点类型,可以直接使用对应的配置文件,将对应配置文件需要修改的部分修改一下,然后将上述格式化数据目录命令和启动命令中的配置文件路径改变一下即可,这样可以省略我们设定process.roles和listeners或者控制器节点删除advertise.listeners配置的操作。
4.4、controller.quorum.voters
该配置项用于配置集群中Controller节点选举过程中的投票者,集群中所有的Controller节点都需要被罗列在这个配置项中,其配置格式为id1@host1:port1,id2@host2:port2,id3@host3:port3...
。
有的同学可能认为这里需要把集群中所有节点都写进去,事实上这是错误的,这里只需要写所有的Controller节点和混合节点的id、地址和端口即可,这个配置中配置的端口当然是控制器端口。
上述集群搭建的例子中,由于所有的节点都是混合节点,因此就全部写在其中了!如果我们手动设定每个节点的类型,例如:
节点名 | 节点id | 地址 | 服务器通信端口 | 控制器端口 | 节点类型 |
---|---|---|---|---|---|
Kafka节点1 | 1 | kafka1 | / | 9093 | Controller |
Kafka节点2 | 2 | kafka2 | 9092 | / | Broker |
Kafka节点3 | 3 | kafka3 | 9092 | / | Broker |
那么所有节点的controller.quorum.voters都需要配置为1@kafka1:9093。
事实上,所有的节点都是通过这个配置中的节点列表,来得知所有的控制器节点信息(以获取集群元数据)并得到投票候选者的,因此集群中所有节点,不论是Broker还是Controller,还是混合节点,都需要配置这一项。
4.5、其它配置
除了上述我们涉及到的一些配置之外,还有下列配置大家可以进行修改:
socket.send.buffer.bytes
每次发送的数据包的最大大小(单位:字节)socket.receive.buffer.bytes
每次接收的数据包的最大大小(单位:字节)socket.request.max.bytes
接收的最大请求大小(单位:字节)num.partitions
每个Topic的默认分区数
上述无论是哪个模式的集群,都可以在配置文件中找到这些配置,如果找不到可手动加入。除了修改配置文件之外,我们还可以在启动Kafka的命令中指定配置和值,例如:
bin/kafka-server-start.sh config/server.properties --override zookeeper.connect=127.0.0.1:2181 --override broker.id=1
上述命令在启动时通过命令指定了zookeeper.connect配置值为127.0.0.1:2181,以及broker.id为1,可见在后面追加–override 配置名=值即可,注意命令行中指定的配置值会覆盖掉配置文件中的配置值!
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。