Kafka 原理分析—— Parition的高可用副本机制


我们已经知道 Kafka 的每个 topic 都可以分为多个 Partition,并且多个 Partition 会均匀分布在集群的各个节点上。虽然这种方式能够有效的对数据进行分片,但是对于每个 partition 来说都是单点的。当其中一个 partition 不可用的时候,那么这部分消息就没办法消费。所以 Kafka 为了提高 partition 的可靠性,提供了副本(Replica)的概念,通过副本机制来实现冗余备份。

每个分区可以有多个副本,并且在副本集合中会存在一个 Leader 副本,所有的读写请求都是由 Leader 副本来进行处理。剩余的其他副本都作为 Follower 副本。Follower 副本会从 Leader 副本同步消息日志。这点类似于 Zookeeper 中 leader 和 follower 的概念,但是具体的实现方式还是有比较大的差异。所以我们可以认为,副本集会存在一主多从的关系。

一般情况下,同一个分区的多个副本会均匀分配到集群中的不同 borker 上。当 Leader 副本所在的broker 出现故障后,可以重新选举新的 Leader 副本继续对外提供服务。通过这样的副本机制来提供 Kafka 集群的可用性。

1. 副本分配算法

  1. 将所有 N Broker 和待分配的 i 个 Partition 排序。
  2. 将第 i 个 Partition 分配到第 (i mod n) 个 Broker 上。
  3. 将第 i 个 Partition 的第 j 个 副本分配到第 ((i+j) mod n) 个 Broker上。

创建带 2 个副本的 topic

[dwjf321@hadoop102 kafka]$ bin/kafka-topics.sh --create --zookeeper hadoop102:2181,hadoop103:2181,hadoop10:2181 --replication-factor 2 --partitions 3 --topic secondTopic

然后我们可以在 /opt/module/kafka/logs/路径下看到对应 topic 的副本信息。我们通过一个图形的方式来表达。

针对 secondTopic 这个 topic 的 3 个分区对应的 3个副本

在这里插入图片描述

如何知道哪个分区对应的 Leader 是谁呢?

在 Zookeeper 服务器上,通过如下命令去获取对应分区的信息,比如下面这个获取 secondTopic 第 1 个分区的状态信息。

[zk: localhost:2181(CONNECTED) 3] get  /brokers/topics/secondTopic/partitions/1/state
{
    
    "controller_epoch":125,"leader":0,"version":1,"leader_epoch":0,"isr":[0,1]}
cZxid = 0x410000008e
ctime = Sun Jan 03 16:02:47 CST 2021
mZxid = 0x410000008e
mtime = Sun Jan 03 16:02:47 CST 2021
pZxid = 0x410000008e
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 76
numChildren = 0

Leader 表示当前分区的 Leader是那个 broker-id。

下图中,绿色的线条表示该分区中 Leader 节点。其他节点就为 Follower。

在这里插入图片描述

Kafka 提供了数据复制算法保证,如果 leader 发生故障或挂掉,一个新 leader 被选举并被接受客户端的消息成功写入。Kafka 确保从同步副本列表中选举一个副本为 leader;leader 负责维护和跟踪ISR (in-Sync replicas,副本同步队列) 中所有 follower 滞后的状态。当 producer 发送一条消息到 broker 后,leader 写入消息并复制到所有 follower 。消息提交之后才被成功复制到所有的同步副本。

既然有副本机制,就一定涉及到数据同步的概念,那接下来分析下数据是如何同步的?

2. kafka副本机制中的几个概念

Kafka 分区下有可能有很多个副本 (replica) 用于实现冗余,从而进一步实现高可用。副本根据角色的不同可分为 3类:

  • Leader副本:响应clients端读写请求的副本

  • Follower副本:被动地备份 Leader 副本中的数据,不能响应 clients 端读写请求。

  • ISR:副本同步队列,包含了 Leader 副本和所有与 Leader 副本保持同步的 Follower 副本

    如何判定是否与 Leader 同步后面会提到每个 Kafka 副本对象都有两个重要的属性:LEO 和 HW。注意是所有的副本,而不只是 Leader 副本。

  • LEO:日志末端位移 (log end offset),记录了该副本底层日志 (log) 中下一条消息的位移值。注意是下一条消息!也就是说,如果 LEO=10,那么表示该副本保存了10条消息,位移值范围是[0, 9]。另外,Leader LEO 和 Follower LEO的更新是有区别的。我们后面会详细说。

  • HW:即上面提到的水位值。对于同一个副本对象而言,其HW值不会大于LEO值。小于等于HW值的所有消息都被认为是“已备份”的(replicated)。同理,Leader 副本和 Follower 副本的 HW 更新是有区别的。

3. 副本协同机制

刚刚提到了,消息的读写操作都只会由 Leader节点来接收和处理。Follower 副本只负责同步数据以及当 Leader副本所在的 broker挂了以后,会从 Follower副本中选取新的 Leader。

在这里插入图片描述

写请求首先由 Leader 副本处理,之后 Follower 副本会从 Leader上拉取写入的消息,这个过程会有一定的延迟,导致 Follower 副本中保存的消息略少于 Leader 副本,但是只要没有超出阈值都可以容忍。但是如果一个 Follower 副本出现异常,比如宕机、网络断开等原因长时间没有同步到消息,那这个时候,Leader 就会把它踢出去。kafka 通过 ISR 集合来维护一个分区副本信息。

4. 副本同步队列(ISR)

ISR 表示目前可用消息量与 Leader 相差不多的副本集合,这是整个副本集合的一个子集。怎么去理解可用和相差不多这两个词呢?具体来说,ISR 集合中的副本必须满足两个条件

  1. 副本所在节点必须维持着与 Zookeeper 的连接。

  2. 副本最后一条消息的 offset 与 leader 副本的最后一条消息的 offset 之间的差值不能超过指定的阈值 (replica.lag.time.max.ms)
    replica.lag.time.max.ms:如果该 Follower 在此时间间隔内一直没有追上过 Leader 的所有消息,则该Follower 就会被剔除 ISR 列表
    ISR 数据保存在 Zookeeper 的/brokers/topics/<topic>/partitions/<partitionId>/state节点中

5. 水位值 (HW) 和 日志末端位移 (LED)

关于 Follower 副本同步的过程中,还有两个关键的概念,HW (High Watermark) 和 LEO (Log End Offset)。这两个参数跟 ISR集合 紧密关联。HW 标记了一个特殊的 offset,当消费者处理消息的时候,只能拉去到 HW 之前的消息,HW之后的消息对消费者来说是不可见的。也就是说,取 Partition 对应 ISR 中最小的 LEO 作为 HW,Consumer最多只能消费到 HW 所在的位置。每个 replica 都有 HW,Leader 和 Follower 各自维护更新自己的HW 的状态。一条消息只有被 ISR 里的所有 Follower 都从 Leader 复制过去才会被认为已提交。这样就避免了部分数据被写进了 Leader,还没来得及被任何 Follower 复制就宕机了,而造成数据丢失(Consumer 无法消费这些数据)。而对于 Producer 而言,它可以选择是否等待消息 commit,这可以通过 acks 来设置。这种机制确保了只要 ISR 有一个或以上的 Follower,一条被 commit 的消息就不会丢失。

6. 数据的同步过程

了解了副本的协同过程以后,还有一个最重要的机制,就是数据的同步过程。它需要解决:

  1. 怎么传播消息

  2. 在向消息 producer 返回 ack 之前需要保证多少个Replica 已经接收到这个消息

数据的处理过程是

Producer 在发布消息到某个 Partition 时,先通过 ZooKeeper 找到该Partition的Leader【get/brokers/topics/<topic>/partitions/2/state】,然后无论该 Topic 的 ReplicationFactor 为多少(也即该 Partition 有多少个Replica),Producer 只将该消息发送到该 Partition 的 Leader。Leader 会将该消息写入其本地 Log。每个 Follower 都从 Leader pull 数据。这种方式上,Follower 存储的数据顺序与Leader保持一致。Follower 在收到该消息并写入其 Log 后,向 Leader 发送 ACK。一旦 Leader 收到了 ISR 中的所有Replica 的 ACK,该消息就被认为已经 commit 了,Leader 将增加 HW (High Watermark) 并且向 Producer 发送ACK。

猜你喜欢

转载自blog.csdn.net/dwjf321/article/details/114280148