乐趣区

关于zookeeper:分布式系统的脑裂到底是个什么玩意

目前大多数我的项目都在往分布式上倒退,一旦零碎采纳分布式系统,便会引入更多简单场景和解决方案。比方,当你在零碎中应用了 Elasticsearch、ZooKeeper 集群时,你是否理解过集群的“脑裂”景象?又是否晓得它们是如何解决脑裂问题的?

如果这些都还未理解,那么你对分布式的理解过于表象了,举荐你读一读这篇文章。

上面就以 zookeeper 为例,带大家理解一下分布式系统中的脑裂景象及如何解决。

什么是脑裂?

在 Elasticsearch、ZooKeeper 这些集群环境中,有一个独特的特点,就是它们有一个“大脑”。比方,Elasticsearch 集群中有 Master 节点,ZooKeeper 集群中有 Leader 节点。

集群中的 Master 或 Leader 节点往往是通过选举产生的。在网络失常的状况下,能够顺利的选举出 Leader(后续以 Zookeeper 命名为例)。但当两个机房之间的网络通信呈现故障时,选举机制就有可能在不同的网络分区中选出两个 Leader。当网络复原时,这两个 Leader 该如何解决数据同步?又该听谁的?这也就呈现了“脑裂”景象。

艰深的讲,脑裂 (split-brain) 就是“大脑决裂”,原本一个“大脑”被拆分成两个或多个。试想,如果一个人有多个大脑,且互相独立,就会导致人体“载歌载舞”,“不听使唤”。

理解了脑裂的基本概念,上面就以 zookeeper 集群的场景为例,来剖析一下脑裂的产生。

zookeeper 集群中的脑裂

咱们在应用 zookeeper 时,很少遇到脑裂景象,是因为 zookeeper 曾经采取了相应的措施来缩小或防止脑裂的产生,这个前面会讲到 Zookeeper 的具体解决方案。当初呢,先假如 zookeeper 没有采取这些避免脑裂的措施。在这种状况下,看看脑裂问题是如何产生的。

现有 6 台 zkServer 服务组成了一个集群,部署在 2 个机房:

失常状况下,该集群只有会有个 Leader,当 Leader 宕掉时,其余 5 个服务会从新选举出一个新的 Leader。

如果机房 1 和机房 2 之间的网络呈现故障,临时不思考 Zookeeper 的过半机制,那么就会呈现下图的状况:

也就是说机房 2 的三台服务检测到没有 Leader 了,于是开始从新选举,选举出一个新 Leader 来。本来一个集群,被分成了两个集群,同时呈现了两个“大脑”,这就是所谓的“脑裂”景象。

因为本来的一个集群变成了两个,都对外提供服务。一段时间之后,两个集群之间的数据可能会变得不统一了。当网络复原时,就面临着谁当 Leader,数据怎么合并,数据抵触怎么解决等问题。

当然,下面的过程只是咱们假如 Zookeeper 不做任何预防脑裂措施时会呈现的问题。那么,针对脑裂问题,Zookeeper 是如何进行解决的呢?

Zookeeper 的过半准则

避免脑裂的措施有多种,Zookeeper 默认采纳的是“过半准则”。所谓的过半准则就是:在 Leader 选举的过程中,如果某台 zkServer 取得了超过半数的选票,则此 zkServer 就能够成为 Leader 了。

底层源码实现如下:

public class QuorumMaj implements QuorumVerifier {
 
    int half;
    
    // QuorumMaj 构造方法。// 其中,参数 n 示意集群中 zkServer 的个数,不包含观察者节点
    public QuorumMaj(int n){this.half = n/2;}

    // 验证是否合乎过半机制
    public boolean containsQuorum(Set<Long> set){
        // half 是在构造方法里赋值的
        // set.size()示意某台 zkServer 取得的票数
        return (set.size() > half);
    }
}

上述代码在构建 QuorumMaj 对象时,传入了集群中无效节点的个数;containsQuorum 办法提供了判断某台 zkServer 取得的票数是否超过半数,其中 set.size 示意某台 zkServer 取得的票数。

上述代码外围点两个:第一,如何计算半数;第二,投票属于半数的比拟。

以上图 6 台服务器为例来进行阐明:half = 6 / 2 = 3,也就是说选举的时候,要成为 Leader 至多要有 4 台机器投票才可能选举胜利。那么,针对下面 2 个机房断网的状况,因为机房 1 和机房 2 都只有 3 台服务器,根本无法选举出 Leader。这种状况下整个集群将没有 Leader。

在没有 Leader 的状况下,会导致 Zookeeper 无奈对外提供服务,所以在设计的时候,咱们在集群搭建的时候,要防止这种状况的呈现。

如果两个机房的部署申请部署 3:3 这种情况,而是 3:2,也就是机房 1 中三台服务器,机房 2 中两台服务器:

在上述情况下,先计算 half = 5 / 2 = 2,也就是须要大于 2 台机器能力选举出 Leader。那么此时,对于机房 1 能够失常选举出 Leader。对于机房 2 来说,因为只有 2 台服务器,则无奈选出 Leader。此时整个集群只有一个 Leader。

对于上图,颠倒过去也一样,比方机房 1 只有 2 台服务器,机房 2 有三台服务器,当网络断开时,选举状况如下:

Zookeeper 集群通过过半机制,达到了要么没有 Leader,要没只有 1 个 Leader,这样就防止了脑裂问题。

对于过半机制除了可能避免脑裂,还能够实现疾速的选举。因为过半机制不须要期待所有 zkServer 都投了同一个 zkServer 就能够选举出一个 Leader,所以也叫疾速领导者选举算法。

新旧 Leader 抢夺

通过过半准则能够避免机房分区时导致脑裂景象,但还有一种状况就是 Leader 假死。

假如某个 Leader 假死,其余的 followers 选举出了一个新的 Leader。这时,旧的 Leader 复活并且依然认为本人是 Leader,向其余 followers 收回写申请也是会被回绝的。

因为 ZooKeeper 保护了一个叫 epoch 的变量,每当新 Leader 产生时,会生成一个 epoch 标号(标识以后属于那个 Leader 的统治期间),epoch 是递增的,followers 如果确认了新的 Leader 存在,晓得其 epoch,就会回绝 epoch 小于现任 leader epoch 的所有申请。

那有没有 follower 不晓得新的 Leader 存在呢,有可能,但必定不是大多数,否则新 Leader 无奈产生。ZooKeeper 的写也遵循 quorum 机制,因而,得不到大多数反对的写是有效的,旧 leader 即便各种认为本人是 Leader,仍然没有什么作用。

ZooKeeper 集群节点为什么要部署成奇数

下面讲了过半准则,因为 Zookeeper 默认采纳的就是这种策略,那就带来另外一个问题。集群的数量设置为多少适合呢?而咱们所看到的 Zookeeper 节点数个别都是奇数,这是为什么呢?

首先,只有集群中有过半的机器是失常工作的,那么整个集群就可对外服务。那么咱们列举一些状况,来看看在这些状况下集群的容错性。

如果有 2 个节点,那么只有挂掉 1 个节点,集群就不可用了。此时,集群对的容忍度为 0;

如果有 3 个节点,那么挂掉 1 个节点,还有剩下 2 个失常节点,超过半数,能够从新选举,失常服务。此时,集群的容忍度为 1;

如果有 4 个节点,那么挂掉 1 个节点,剩下 3 个,超过半数,能够从新选举。但如果再挂掉 1 个,只剩下 2 个,就无奈失常选举和服务了。此时,集群的容忍度为 1;

顺次类推,5 个节点,容忍度为 2;6 个节点容忍度同样为 2;

既然 3 个节点和 4 个节点、5 个节点和 6 个节点,也就是 2n 和 2n- 1 的容忍度是一样的,都是 n -1。那么,为了节俭资源,为了更加高效(更多节点参加选举和通信),为什么不少一个节点呢?这就是为什么集群要部署成奇数的起因。

解决脑裂的常见办法

下面提到了 Zookeeper 应用的过半准则,这里再把解决脑裂问题的场景形式总结一下。

办法一,Quorums(法定人数)形式

比方 3 个节点的集群,Quorums = 2,也就是说集群能够容忍 1 个节点生效,这时候还能选举出 1 个 lead,集群还可用。比方 4 个节点的集群,它的 Quorums = 3,Quorums 要超过 3,相当于集群的容忍度还是 1,如果 2 个节点生效,那么整个集群还是有效的。这是 ZooKeeper 避免“脑裂”默认采纳的办法。

办法二,增加心跳线

集群中采纳多种通信形式,避免一种通信形式生效导致集群中的节点无奈通信。

比方,增加心跳线。原来只有一条心跳线路,此时若断开,则接管不到心跳报告,判断对方曾经死亡。若有 2 条心跳线路,一条断开,另一条依然可能接管心跳报告,能保障集群服务失常运行。心跳线路之间也能够 HA(高可用),这两条心跳线路之间也能够相互检测,若一条断开,则另一条马上起作用。失常状况下,则不起作用,节约资源。

办法三,启动磁盘锁定形式。

应用磁盘锁的模式,保障集群中只能有一个 Leader 获取磁盘锁,对外提供服务,防止数据错乱产生。然而,也会存在一个问题,若该 Leader 节点宕机,则不能被动开释锁,那么其余的 Follower 就永远获取不了共享资源。于是有人在 HA 中设计了 ” 智能 ” 锁。正在服务的一方只有在发现心跳线全副断开(觉察不到对端)时才启用磁盘锁。平时就不上锁了

办法四,仲裁机制形式。

脑裂导致的结果是从节点不晓得该连贯哪一台 Leader,此时有一个仲裁方就能够解决此问题。比方提供一个参考的 IP 地址,心跳机制断开时,节点各自 ping 一下参考 IP,如果 ping 不通,那么示意该节点网络曾经呈现问题,则该节点须要自行退出争抢资源,开释占有的共享资源,将服务的提供性能让给性能更全面的节点。

以上形式能够同时应用,能够缩小集群中脑裂状况的产生,但不能齐全保障,比方仲裁机制中 2 台机器同时宕机,那么此时集群中没有 Leader 能够应用。此时就须要人工干预了。

小结

咱们常常在说,咱们的零碎应用了分布式,但咱们真的理解分布式中场景的一些场景和解决方案吗?通过本文对脑裂场景的剖析及解决方案介绍,你学到了吗?来一起学习吧。

博主简介:《SpringBoot 技术底细》技术图书作者,热爱钻研技术,写技术干货文章。

公众号:「程序新视界」,博主的公众号,欢送关注~

技术交换:请分割博主微信号:zhuan2quan

退出移动版