Broker
Broker 注册
Kafka 强依赖于 zookeeper ,每当一个 broker 启动时,它会将自己注册到 zookeeper 的临时节点。
Broker 的注册路径为 chroot/brokers/ids/<broker.id>
,如果没有配置 chroot,则路径是 /brokers/ids/<broker.id>
。是否配置了 chroot 取决于 server.properties
中的zookeeper.connect
参数是否设置了 chroot。
broker 向 zookeeper 中注册的信息以 json 格式保存。其中包括的信息如下:
{
"listener_security_protocol_map": {
"PLAINTEXT": "PLAINTEXT"
},
"endpoints": [
"PLAINTEXT://192.168.110.110:9092"
],
"jmx_port": 9999,
"host": "192.168.110.110",
"timestamp": "1575450860777",
"port": 9092,
"version": 4
}
复制代码
listener_security_protocol_map
:指定该 broker 与外界通信所用的安全协议类型,kafka 支持 broker 使用不同的安全协议与 clients 和其他 broker 通信endpoints
:指定 broker 的服务 endpoint 列表,每个 endpoints 指明了传输安全协议类型、broker 主机名和端口信息- jmx_port:broker 的 JMX 监控端口,需要在启动 broker 前设置 jmx_port 环境变量
host
:broker 主机名或 IP 地址。timestamp
:broker 启动的时间。port
:broker 服务端口号。version
:broker 注册信息的版本,注意这不是 kafka 的版本
Broker 生命周期管理
Kafka 利用 zookeeper 临时节点来管理 broker 生命周期。
诞生
broker 启动时在 zookeeper 中创建对应的临时节点,同时还会创建一个监听器。
监听变化
broker临时节点上的监听器会实时监听节点的状态,一旦 broker 发生变化,监听器会将状态信息同步到 kafka 集群上
死亡
一旦 broker 崩溃,它与 zookeeper 的会话就会失效,导致该临时节点被删除,监听器被触发,然后处理后续事宜。
Controller Broker
从客户端角度来看, KafKa 一种对等的分布式架构,因为每个 Broker 都包含整个集群的元信息,都可以提供完整的服务,但站在服务端的角度来看,kafka 却是主从架构,至于原因,这就要说到 Controller Broker 了
什么是 Controller Broker
KafKa Broker 有两种角色, Controller 和 Follower ,顾名思义 Controller 是主Follower 是从,两种角色做的事情大部分都是一样的,只是 Controller 还需要负责一些管理相关的工作
Controller Broker 的职责
Follower Broker 要做的事情它都要做 ,此外 Controller Broker 还要负责:
Topic 管理
- 创建、删除 Topic
- leader Replication 选举。例如某个分区的 leader 副本出现故障时,由 Controller Broker 负责为该分区选举新的 leader 副本
- 分区重分配。例如当使用 kafka-topic.sh 脚本为某个 topic 增加分区数量时,由 Controller 负责分区的重新分配
- 同步元数据。例如某个分区的 ISR 集合发生变化时,由 Controller 负责通知所有 broker 更新元数据信息
- preferred leader 选举。preferred leader选举是为了避免部分 Broker 负载过重而提供的一种换 Leader 的方案。
- …
管理 Broker 集群
- 上线新加入的 Broker
- 下线故障的 Broker
- …
Controller Broker 选举
每个 Broker 启动后都会尝试到 Zookeeper 中创建一个 /controller 的临时节点,谁先创建成功谁就是 Controller,其他的 Broker 都是 Follower
/controller 的临时节点的信息如下
{
"version": 1,
"brokerid": 2,
"timestamp": "1569267532839"
}
复制代码
version
: 固定为 1(代码中设置固定值 1)brokerid
: 对应该 broker 临时节点的 idtimestamp
: 注册时间
当 Controller Broker 和 zookeeper 失去连接时,临时节点会删除,而其他 broker 会监听该节点的变化,当节点删除时,其他 broker 会收到事件通知,重新发起 controller 选举
主从架构中的脑裂
主从架构中如果主节点挂了,从节点会通过一些方式升级为主节点,但这里的问题是如何分辨主机点是真的挂了,还是暂时失联,如果只是暂时失联而从节点又升级为主节点,待原来的的主节点恢复后,集群中就会有两个主节点,后果就是集群中的其他从节点不知道该听谁的命令,这就是脑裂!
Controller 脑裂
首先每个 broker 都会在内存中保存当前 /controller
节点的 brokerid
,这个值被称为 activeControllerId
。
有新的 broker 当选 Controller 后,会去刷新 /controller
节点,/controller
节点发生变化就会触发监听事件,每个 broker (也包括原来的 Controller broker) 都会更新内存中保存的 activeControllerId
。
如果原来的 Controller broker 还能监听到事件,就会更新 activeControllerId
,并检查 brokerid
值与新的 activeControllerId
值是否一致,如果不一致,就“退位”,关闭相应的资源,比如关闭状态机、注销相应的监听器等。
以上理想情况,旧 Controller broker 还能监听到事件,因此整个交接过程还比较顺利。但大部分情况下都是 Controller broker 长时间失去“响应”,才会发生 Controller,这个种情况下旧 Controller broker 是无法处理监听事件的,如果是真挂掉那还好说,如果是假死,选举完成之后又恢复了,集群中就会有两个 Controller ,这就是 Controller 的脑裂
KafKa 解决脑裂
kafka 除了在 zookeeper 中注册了 /controller
这个临时节点,还注册了 /controller_epoch
这个持久节点,该节点中存放的是一个整型的 controller_epoch 值,记录了当前是第几代 Controller ,因此被称为控制器的纪元”,初始值为 1,每次发生 Controller 变更就加1
每个和 Controller 交互的请求都会携带 controller_epoch 这个字段,如果请求的 controller_epoch 值小于内存中的 controller_epoch 值,则认为这个请求是向已经过期的 Controller 所发送请求,那么这个请求会被认为是无效的请求。如果这个请求的 controller_epoch 值大于内存中的 controller_epoch 值,那么说明已经有新的控制器当选了。
kafka 通过 controller_epoch 来保证 Controller 的唯一性,进而保证相关操作的唯一性。