Kafka - Server

73 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情

1、Broker

构成Kafka服务的服务进程叫做Broker,一个Kafka集群由多个Broker构成,这些Broker常常位于不同的服务器上,以实现集群的高可用性。当然单机上也能启动多个Broker。

1.1 、 Broker生命周期

2.8版本之前,Kafka 强依赖于 zookeeper ,也利用 zookeeper 临时节点来管理 broker 生命周期:

  1. Broker启动时,将在 zookeeper 中创建对应的临时节点,同时还会创建一个监听器
  2. Broker生命周期内,其创建的监听器会实时监听节点的状态,一旦 Broker 发生变化,监听器会将状态信息同步到 Kafka 集群上
  3. Broker死亡时,它与 zookeeper 的会话就会失效,导致该临时节点被删除,监听器被触发,然后处理后续事宜

1.2、 Broker的主从架构

Kafka集群中的每一个Broker都拥有整个集群的元信息,都可以对外提供完整的服务。 但Broker之间仍存在主从区分。分为Controller Broker和Follower Broker:

1.2.1、 Controller Broker

负责Kafka中Topic的管理及Broker集群的管理。

Topic的管理:

  1. 创建删除Topic
  2. Topic中Partition的分配
  3. Topic中Leader Replica的选举
  4. Preferred Leader的选举
  5. .....

集群的管理:

  1. 新增 Broker
  2. Broker 主动关闭
  3. Broker 故障
  4. .....

1.2.2、 Follower Broker

集群中基础的Broker,能够对外提供服务。同时监听ZooKeeper 上的/controller节点,同步Controller Broker信息

1.2.3、 Controller Broker的分配

  1. 当Kafka集群启动时,多个Broker均会尝试去 ZooKeeper 中创建 /controller 节点,成功创建/controller节点的Broker成为Controller Broker
  2. 当Controller Broker发生故障或是其他情况,与ZooKeeper断开连接,临时节点消失。集群中的其他节点收到 watch 对象发送控制器下线的消息后,其他 broker 节点都会尝试让自己去成为新的控制器,具体过程同情况1。

1.2.4、 脑裂

当Controller Broker由于网络波动导致的临时断线,导致Kafka集群中选举出新的Controller Broker,此时Kafka集群中同时存在了两个Controller Broker,这就是脑裂。

Kafka提供了以下办法用于解决脑裂:

  1. activeControllerId: 每一个Broker均会watch /controller节点,当新的Controller Broker被选举出后,将会触发事件,通知集群中所有的Broker当前的activeControllerId。
  2. controller_epoch: Kafka在ZooKeeper中注册了 /controller_epoch这个持久节点,其中记录着Controller的变更次数。 每一个Controller Broker内存中都存放着注册成功时的controller_epoch值,同时Controller每次的交互请求均能获取到controller_epoch的最新值,只需要对比内存值和最新值,Controller即可知道自己是否被 “干掉了”

2、高可用实现

前面谈论到了多服务、多Broker构成的Kafka,这样搭建Kafka集群提高了集群的稳定性和可用性。 同时Kafka内部也提供了不同的机制用于实现高可用性:

2.1、 Partition 分区

和大多分布式系统一样,Kafka也提供了分区、分片的概念实现。 Kafka将一个Topic拆分为多个Partition,消息将被存储到不同的分区中。同时,分区也将分布在不同的Broker中。

修改Kafka配置文件 num.partitions 参数以设置全局Topic分区个数,也可以在Topic创建时使用 –partitions 指定分区数

2.1.1、 分区策略

  1. 轮询策略: 轮询分区以存储消息,是Kafka在无消息key的时候的默认分区策略
  2. 随机策略: 随机的将消息存储到各个分区中
  3. 消息键策略: 通过消息的key作为依据存储到分区中,相同key的数据一定会存储在相同的分区中, 是Kafka在存在消息key时的默认分区策略
  4. 自定义策略: 通过实现类(org.apache.kafka.clients.producer.Partitioner),并在Producer端显式设置自定义类以启用

2.1.2、分区中消息的Offset

Partition 中的每条记录都会被分配一个唯一的序号,称为 Offset。Offset 是一个递增的、不可变的数字,由 Kafka 自动维护。当一条记录写入 Partition 的时候,它就被追加到 log 文件的末尾,并被分配一个序号,作为 Offset。

由于消息的Offsets存在,因此分区中的消息是有序的。但从Topic层面(多分区的)而言,消息仍是无序的,因此在实现强顺序消息业务时,应该将同一业务消息存储到同一个分区中,以保证消息的有序性。

2.2、 Replica 副本

为了提高系统稳定性,Kafka实现了副本冗余存储,允许在少量Broker宕机时仍能提供有效的服务。

Kafka允许存在一个Leader Replica和多个Follower Replica。 但特别的是,追随者副本仅仅只同步领导者副本的数据,而不向外提供服务。

image2022-8-24_15-12-5.png

追随者副本不提供服务,其原因个人觉得在于两点:

  1. 方便了Read-your-writes的实现,追随副本为定时拉取领导副本的数据,若追随者提供服务,较难实现消息的即时性。
  2. 如果多个副本同步速度不相符,可能出现脏读等现象
  3. 消息在消费时也需要维护Offset值,如果允许在不同的副本中消费,还需要维护多个副本协同的Offset的准确值。

2.2.1、 副本的同步与ISR

由于追随者副本为定期地异步拉取领导者副本中的数据进行同步,必然是有可能存在与领导者副本数据不同步的情况。

为了区分追随者副本是否与领导者副本同步,Kafka引入了In-sync Replicas (ISR),存在于ISR中的副本均被认为是与领导者副本同步的。同时,领导者副本天生也就存在于ISR中。

而ISR中“与领导者副本同步”的定义,便是副本与领导者副本消息差异(落后)时间低于设置值(replica.lag.time.max.ms),即哪怕副本中存在的消息极少,但只要其与领导者副本消息差异时间在规定以内,都视为其与领导者副本是同步的。

当副本与领导者副本消息差异时间高于设置值后,Kafka会将该副本移出ISR中;同样的,当不在ISR中的副本满足了加入ISR的要求,Kafka也会将其加入到ISR中。Kafka的ISR的管理最终都会反馈到Zookeeper节点上。具体位置为:/brokers/topics/[topic]/partitions/[partition]/state。

2.2.2、 Leader Election 领导者选举

当ISP为空时,即Leader Replica也挂掉时,Controller Broker利用配置的LeaderSelector对非同步副本进行Leader的重新选举。

但是由于非同步副本数据与领导副本数据可能不一致,因此Unclean Leader Election可能导致消息的丢失,我们可以使用 unclean.leader.election.enable 关闭 Unclean Leader Election,但同样的,我们也将要承受无法对外提供该Topic服务的结果。