关于java:Kafka高可用架构设计

4次阅读

共计 6423 个字符,预计需要花费 17 分钟才能阅读完成。

1. 高可用概述

高可⽤性 (High Availability),指零碎无间断地执⾏其性能的能力,代表零碎的可⽤性水平。Kafka 从 0.8 版本开始提供了高可⽤机制,可保障⼀个或多个 Broker 宕机后,其余 Broker 及所有 Partition 都能持续提供服务,且存储的音讯不失落。
对分布式系来说,当集群规模回升到肯定水平后,一台或者多台机器宕机的可能性⼤大减少;Kafka 采⽤多机备份和音讯应答确认形式解决了数据失落问题,并通过一套失败复原机制解决服务不可⽤问题。

2. 音讯备份机制

2.1 音讯备份

Kafka 容许同⼀个 Partition 存在多个音讯正本 (Replica),每个 Partition 的正本通常由 1 个 Leader 及 0 个以上的 Follower 组成,⽣产者将 音讯间接发往对应 Partition 的 Leader,Follower 会周期地向 Leader 发送同步申请,Kafka 的 Leader 机制在保障数据⼀致性地同时升高了了 音讯备份的复杂度。
同⼀ Partition 的 Replica 不应存储在同一个 Broker 上,因为一旦该 Broker 宕机,对应 Partition 的所有 Replica 都无奈⼯作,这就达不到 高可用的成果。为了做好负载平衡并提⾼容错能力,Kafka 会尽量将所有的 Partition 以及各 Partition 的正本平均地调配到整个集群上。举个例⼦,当集群中部署了 3 台 Broker,TopicA 共有 4 个 Partition,每个 Partition 均有 3 个 Replica 时下图就是⼀种正当理的散布形式。

2.2 ISR

ISR(In-Sync Replicas)指的是⼀个 Partition 中与 Leader“放弃同步”的 Replica 列表(理论存储的是正本所在 Broker 的 BrokerId),这里的 放弃同步不是指与 Leader 数据放弃齐全⼀统一,只需在 replica.lag.time.max.ms 工夫内与 Leader 放弃无效连贯,官⽅方解释如下
If a follower hasn’t sent any fetch requests or hasn’t consumed up to the leaders log end offset for at least this time, the leader will remove the follower from isr,(default value =10000)
Follower 周期性地向 Leader 发送 FetchRequest 申请(数据结构⻅见下),发送工夫距离配置在 replica.fetch.wait.max.ms 中,默认值为 500。

public class FetchRequest {
    private final short versionId;
    private final int correlationId;
    private final String clientId;
    private final int replicaId;
    private final int maxWait; // Follower 容忍的最⼤大等待时间: 到点 Leader ⽴立刻返回后果,默认值 500
    private final int minBytes; // Follower 容忍的最⼩小返回数据⼤大⼩小: 当 Leader 有⾜足够数据时⽴立刻返回,兜底期待 ma private final Map<TopicAndPartition, PartitionFetchInfo> requestInfo; // Follower 中各 Partititon
}

各 Partition 的 Leader 负责保护 ISR 列表并将 ISR 的变更同步至 ZooKeeper,被移出 ISR 的 Follower 会持续向 Leader 发 FetchRequest 请 求,试图再次跟上 Leader 从新进⼊入 ISR。
ISR 中所有正本都跟上了 Leader,通常只有 ISR 里的成员才可能被选为 Leader。当 Kafka 中 unclean.leader.election.enable 配置为 true(默认值为 false)且 ISR 中所有正本均宕机的状况下,才容许 ISR 外的正本被选为 Leader,此时会失落局部已应答的数据。

2.3 Acks

为了了讲清楚 ISR 的作⽤用,下⾯面介绍⼀下⽣产者能够抉择的音讯应答⽅式,⽣产者发送音讯中蕴含 acks 字段,该字段代表 Leader 应答⽣产者前 Leader 收到的应答数

  • acks = 0

⽣产者⽆需期待服务端的任何确认,音讯被增加到生产者套接字缓冲区后就视为已发送,因而 acks= 0 不不能保障服务端已收到音讯,使⽤用场景较少,本⽂文不不做任何探讨

  • acks = 1

Leader 将音讯写⼊入本地⽇日志后⽆无需期待 Follower 的音讯确认就做出应答。如果 Leader 在应答音讯后⽴立刻宕机且其余 Follower 均未完 成音讯的复制,则该条音讯将失落

上图左侧的稳态场景下,Partition1 的数据冗余备份在 Broker0 和 Broker2 上;Broker0 中的正本与 Leader 正本因⽹网络开销等因素存在 1 秒钟同步时间差,Broker0 中的正本落后 124 条音讯;Broker2 中的正本存在 8 秒钟同步时间差,Broker2 中的正本落后 7224 条音讯。若 图中的 Broker1 忽然宕机且 Broker0 被选为 Partition1 的 Leader,则在 Leader 宕机前写入的 124 条音讯未同步⾄至 Broker0 中的正本,这次宕 机会造成大量量音讯失落。

  • acks = all

Leader 将期待 ISR 中的所有正本确认后再做出应答,因而只有 ISR 中任何⼀一个正本还存活着,这条应答过的音讯就不会失落。acks=all 是可⽤用性最⾼的抉择,但期待 Follower 应答引入了额定的响应工夫。Leader 须要期待 ISR 中所有正本做出应答,此时响应工夫 取决于 ISR 中最慢的那台机器,下图中因复制产生的额定提早为 3 秒。

Broker 的配置项 min.insync.replicas(默认值为 1)代表了失常写⼊⽣产者数据所须要的起码 ISR 个数,当 ISR 中的正本数量⼩于 min.insync.replicas 时,Leader 停⽌止写⼊⽣产者⽣产的音讯,并向⽣产者抛出 NotEnoughReplicas 异样,阻塞期待更更多的 Follower 赶上 并从新进⼊ ISR。被 Leader 应答的音讯都至多有 min.insync.replicas 个正本,因而可能容忍 min.insync.replicas- 1 个正本同时宕机。小结: 发送的 acks= 1 音讯会呈现失落状况,为不失落音讯可配置⽣产者 acks=all & min.insync.replicas >= 2

2.4 LEO & HW

每个 Kafka 正本对象都有上面两个重要属性:

  • LEO(log end offset),即⽇志末端偏移,指向了正本日志中下⼀条音讯的位移值(即下一条音讯的写⼊地位)
  • HW(high watermark),即已同步音讯标识,因其相似于⽊桶效应中短板决定⽔位高度,故取名高⽔位线
    所有⾼⽔位线以下音讯都是已备份过的,消费者仅可生产各分区 Leader ⾼水位线以下的音讯,对于任何⼀个正本对象而⾔其 HW 值不会大于 LEO 值
    Leader 的 HW 值由 ISR 中的所有备份的 LEO 最小值决定(Follower 在发送 FetchRequest 时会在 PartitionFetchInfo 中会携带 Follower 的 LEO)

Kafka 本来使⽤用 HW 来记录正本的备份进度,HW 值的更新通常须要额定一轮 FetchRequest 能力实现,存在一些边缘案例导致备份数据失落或导致多个备份间的数据不统一。本⽂次要介绍可用性,数据⼀一致性及截断规定不详述。Kafka 新引入了 Leader epoch 解决 HW 截断产⽣的问题,有趣味的同学可参考 Apache : Fix log divergence after fast leader fail over

3. 故障复原

3.1 Broker 故障复原

Kafka 从 0.8 版本开始引⼊了一套 Leader 选举及失败复原机制: ⾸先须要在集群所有 Broker 中选出⼀个 Controller,负责各 Partition 的 Leader 选举以及 Replica 的重新分配。当呈现 Leader 故障后,Controller 会将 Leader/Follower 的变动告诉到需为此作出响应的 Broker。
Kafka 使⽤ ZooKeeper 存储 Broker、Topic 等状态数据,Kafka 集群中的 Controller 和 Broker 会在 ZooKeeper 指定节点上注册 Watcher(事件监听器器),以便在特定事件触发时,由 ZooKeeper 将事件告诉到对应 Broker。

3.1.1 Broker 故障场景剖析

  • 场景 1 Broker 与其余 Broker 断开连接

上图中 Broker0 和其余 Broker 都断开了连贯,因为 ZooKeeper 还能接管到 Broker0 的⼼跳,因而 ZooKeeper 认为 Broker0 仍然存活,则对于

Partition0

Broker0 中的正本为 Partition0 的 Leader,当 Broker0 超过 replica.lag.time.max.ms 没接管到 Broker1、Broker2 的 FetchRequest 申请后,Broker0 抉择将 Partition0 的 ISR 膨胀到仅剩 Broker0 自身,并将 ISR 的变更更同步到 ZooKeeper;Broker0 须要依据 min.insync.replicas 的配置决定是否持续承受生产者数据
Partition1
超过 replica.lag.time.max.ms 后,Broker1 会将 Broker0 中的正本从 Partition1 的 ISR 中移除。若后续 Broker0 复原连贯并赶上了了 Broker1,则 Broker1 还会再将 Broker0 从新加⼊入 Partition1 的 ISR

  • 场景 2 Broker 与 ZooKeeper 断开连接

Broker0 与 ZooKeeper 断开连接后,ZooKeeper 会⾃主动删除该 Broker 对应节点,并且认为 Broker0 曾经宕机,则对于

Partition0

ZooKeeper 删除节点后,该节点上注册的 Watcher 会告诉 Controller,Controller 会发现 Broker0 为 Partition0 的 Leader,于是从以后 存活的 ISR 中抉择了了 Broker2 作为 Partition0 的新 Leader。Controller 通过 LeaderAndIsrRequest 将 Leader 变更更告诉到 Broker1、Broker2,于是 Broker1 改向 Broker2 发送 Partition0 数据的 FetchRequest 申请。
⽣生产者每隔 60 秒会从 bootstrap.servers 中的 Broker 获取最新的 metadata,当发现 Partition0 的 Leader 发⽣生变更更后,会改向新 Leader-Broker2 发送 Partition0 数据。另⼀一边,Broker0 收不到 ZooKeeper 告诉,仍然认为⾃自⼰己是 Partition0 的 Leader; 因为 Broker1、Broker2 不不再向 Broker0 发送 FetchRequest 申请,缺失了了 ISR 应答的 Broker0 停⽌止写⼊入 acks=all 的音讯,但能够持续写⼊入 acks= 1 的音讯。在 replica.lag.time.max.ms 工夫后,Broker0 尝试向 ZooKeeper 发送 ISR 变更更申请但失败了了,于是不不再接管⽣生产者的音讯。
当 Broker0 与 ZooKeeper 复原连贯后,发现⾃自⼰己不不再是 Partition0 的 Leader,于是将本地⽇日志截断(为了了保障和 Leader 数据⼀一致性),并开始向 Broker2 发送 FetchRequest 申请。在 Broker0 与 ZooKeeper 失联期间写⼊入 Broker0 的所有音讯因为未在新 Leader 中备份,这些 音讯都失落了了。

Partition1

Broker0 中的正本只是作为 Partition1 的 Follower 节点,⽽而 Broker0 与 Broker1 仍然放弃连贯,因而 Broker0 仍然会向 Broker1 发送 FetchRequest。只有 Broker0 能持续放弃同步,Broker1 也不不会向 ZooKeeper 变更更 ISR。

3.1.2 Broker 故障复原过程

Broker 发⽣生故障后,由 Controller 负责选举受影响 Partition 的新 Leader 并告诉到相干 Broker,具体过程可参考下图。

当 Broker 呈现故障与 ZooKeeper 断开连接后,该 Broker 在 ZooKeeper 对应的 znode 会⾃主动被删除,ZooKeeper 会触发 Controller 注册 在该节点的 Watcher;Controller 从 ZooKeeper 的 /brokers/ids 节点上获取宕机 Broker 上的所有 Partition(简称 set_p);Controller 再从 ZooKeeper 的 /brokers/topics 获取 set_p 中所有 Partition 以后的 ISR; 对于宕机 Broker 是 Leader 的 Partition,Controller 从 ISR 中抉择幸存 的 Broker 作为新 Leader; 最初 Controller 通过 LeaderAndIsrRequest 申请向 set_p 中的 Broker 发送 LeaderAndISRRequest 申请。
受到影响的 Broker 会收到 Controller 发送的 LeaderAndIsrRequest 申请后,Broker 通过 ReplicaManager 的 becomeLeaderOrFollower ⽅办法响应 LeaderAndIsrRequest: 新 Leader 会将 HW 更更新为它的 LEO 值,⽽而 Follower 则通过⼀一系列列策略略截断 log 以保证数据⼀一致性。

3.2 Controller 故障复原

3.2.1 Controller 故障场景剖析

  • 场景 1 Controller 与 ZooKeeper 断开连接

此时 ZooKeeper 会将 Controller 长期节点删除,并依照下节的故障复原过程从新竞选出新 Controller。⽽而本来的 Controller 因为⽆无奈连 上 ZooKeeper,它什什么也执⾏行行不不了了; 当它与 ZooKeeper 复原连贯后发现⾃自⼰己不不再是 Controller,会在 Kafka 集群中充当⼀一个一般的 Broker。

  • 场景 2 Controller 与某个 Broker 断开连接

因为 Controller ⽆无奈告诉到 Broker0,所以 Broker0 不不知道 Partition0 的 Leader 曾经更更换了了,所以也会呈现 3.1.1 节场景 2 形容的呈现短暂 服务不不可⽤用并可能发⽣生数据失落。

3.2.2 Controller 故障复原过程

最初,集群中的 Controller 也会呈现故障,因而 Kafka 让所有 Broker 都在 ZooKeeper 的 Controller 节点上注册⼀一个 Watcher。Controller
发⽣生故障时对应的 Controller 长期节点会⾃主动删除,此时注册在其上的 Watcher 会被触发,所有活着的 Broker 都会去竞选成为新的 Controller(即创立新的 Controller 节点,由 ZooKeeper 保障只会有⼀一个创立胜利)。竞选成功者即为新的 Controller,会在 ZooKeeper 的 下述节点上注册 Watcher,以监控各 Broker 运⾏行行状态、负责 Leader 宕机的失败复原,并对治理理脚本做出响应。

  • 在 /admin 节点上注册 Watcher,以应答治理理员脚本对 Topic 及 Partition 的影响;
  • 在 /brokers/ids 节点上注册 Watcher,以获取各 Brokers 的状态变动;
  • 在 /brokers/topics 节点上注册 Watcher,以监控每个 Partition 的 ISR 正本状态;
正文完
 0