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

容错指的是:当宕掉几个ZooKeeper节点服务器之后,剩下的个数必须大于宕掉的个数,也就是剩下的节点服务数必须大于n/2,这样ZooKeeper集群才可以继续使用,无论奇偶数都可以选举Leader。例如5台ZooKeeper节点机器最多宕掉2台,还可以继续使用,因为剩下3台大于5/2。

至于为什么最好为奇数个节点?这样是为了以最大容错服务器个数的条件下,能节省资源。比如,最大容错为2的情况下,对应的ZooKeeper服务数,奇数为5,而偶数为6,也就是6个ZooKeeper服务的情况下最多能宕掉2个服务,所以从节约资源的角度看,没必要部署6(偶数)个ZooKeeper服务节点。

ZooKeeper 集群有这样一个特性:集群中只要有过半的机器是正常工作的,那么整个集群对外就是可用的。也就是说如果有2个 ZooKeeper 节点,那么只要有1个 ZooKeeper 节点死了,那么 ZooKeeper 服务就不能用了,因为1没有过半,所以2个 ZooKeeper 的死亡容忍度为0;

同理,要是有3个 ZooKeeper,一个死了,还剩下2个正常的,过半了,所以3个ZooKeeper的容忍度为1;同理也可以多列举几个:2->0、3->1、4->1、5->2、6->2,就会发现一个规律,2n和2n-1的容忍度是一样的,都是n-1,所以为了更加高效,何必增加那一个不必要的 ZooKeeper。所以说,根据以上可以得出结论:从资源节省的角度来考虑,ZooKeeper集群的节点最好要部署成奇数个!

ZooKeeper集群中的“脑裂”场景说明

对于一个集群,想要提高这个集群的可用性,通常会采用多机房部署,比如现在有一个由6台zkServer所组成的一个集群,部署在了两个机房:

正常情况下,此集群只会有一个Leader,那么如果机房之间的网络断了之后,两个机房内的zkServer还是可以相互通信的,如果不考虑过半机制,那么就会出现每个机房内部都将选出一个Leader。

这就相当于原本一个集群,被分成了两个集群,出现了两个“大脑”,这就是所谓的“脑裂”现象。对于这种情况,其实也可以看出来,原本应该是统一的一个集群对外提供服务的,现在变成了两个集群同时对外提供服务,如果过了一会,断了的网络突然联通了,那么此时就会出现问题了,两个集群刚刚都对外提供服务了,数据该怎么合并,数据冲突怎么解决等等问题。刚刚在说明脑裂场景时有一个前提条件就是没有考虑过半机制,所以实际上ZooKeeper集群中是不会轻易出现脑裂问题的,原因在于过半机制。

ZooKeeper 的过半机制:在领导者选举的过程中,如果某台 zkServer 获得了超过半数的选票,则此 zkServer 就可以成为Leader了。举个简单的例子:如果现在集群中有5台zkServer,那么half=5/2=2,那么也就是说,领导者选举的过程中至少要有三台zkServer投了同一个zkServer,才会符合过半机制,才能选出来一个Leader。

ZooKeeper选举的过程中为什么一定要有一个过半机制验证?

因为这样不需要等待所有zkServer都投了同一个zkServer就可以选举出来一个Leader了,这样比较快,所以叫快速领导者选举算法。

ZooKeeper过半机制中为什么是大于,而不是大于等于?这就是更脑裂问题有关系了,比如回到上文出现脑裂问题的场景(如上图1):当机房中间的网络断掉之后,机房1内的三台服务器会进行领导者选举,但是此时过半机制的条件是“节点数 > 3”,也就是说至少要4台zkServer才能选出来一个Leader,所以对于机房1来说它不能选出一个Leader,同样机房2也不能选出一个Leader,这种情况下整个集群当机房间的网络断掉后,整个集群将没有Leader。

而如果过半机制的条件是“节点数 >= 3”,那么机房1和机房2都会选出一个Leader,这样就出现了脑裂。这就可以解释为什么过半机制中是大于而不是大于等于,目的就是为了防止脑裂。

如果假设我们现在只有5台机器,也部署在两个机房:

此时过半机制的条件是“节点数 > 2”,也就是至少要3台服务器才能选出一个Leader,此时机房件的网络断开了,对于机房1来说是没有影响的,Leader依然还是Leader,对于机房2来说是选不出来Leader的,此时整个集群中只有一个Leader。因此总结得出,有了过半机制,对于一个ZooKeeper集群来说,要么没有Leader,要么只有1个Leader,这样ZooKeeper也就能避免了脑裂问题。

Zookeeper集群“脑裂”问题处理

什么是脑裂?

简单点来说,脑裂(Split-Brain)就是比如当你的cluster里面有两个节点,它们都知道在这个cluster里需要选举出一个master。那么当它们两个之间的通信完全没有问题的时候,就会达成共识,选出其中一个作为master。但是如果它们之间的通信出了问题,那么两个结点都会觉得现在没有 master,所以每个都把自己选举成 master,于是 cluster里面就会有两个 master。

对于 ZooKeeper 来说有一个很重要的问题,就是到底是根据一个什么样的情况来判断一个节点死亡 down 掉了?在分布式系统中这些都是有监控者来判断的,但是监控者也很难判定其他的节点的状态,唯一一个可靠的途径就是心跳,ZooKeeper 也是使用心跳来判断客户端是否仍然活着。

使用 ZooKeeper 来做 Leader HA 基本都是同样的方式:每个节点都尝试注册一个象征leader 的临时节点,其他没有注册成功的则成为 follower,并且通过 watch 机制监控着Leader 所创建的临时节点,ZooKeeper 通过内部心跳机制来确定 Leader 的状态,一旦Leader 出现意外 Zookeeper 能很快获悉并且通知其他的 follower,其他 flower 在之后作出相关反应,这样就完成了一个切换,这种模式也是比较通用的模式,基本大部分都是这样实现的。

但是这里面有个很严重的问题,如果注意不到会导致短暂的时间内系统出现脑裂,因为心跳出现超时可能是Leader挂了,但是也可能是ZooKeeper节点之间网络出现了问题,导致Leader假死的情况,Leader其实并未死掉,但是与ZooKeeper之间的网络出现问题导致ZooKeeper认为其挂掉了然后通知其他节点进行切换,这样follower中就有一个成为了Leader,但是原本的Leader并未死掉,这时候client也获得Leader切换的消息,但是仍然会有一些延时,ZooKeeper 需要通讯需要一个一个通知,这时候整个系统就很混乱可能有一部分client已经通知到了连接到新的leader上去了,有的client仍然连接在老的Leader上,如果同时有两个client需要对Leader的同一个数据更新,并且刚好这两个client此刻分别连接在新老的Leader上,就会出现很严重问题。

这里做下小总结:

  • 假死:由于心跳超时(网络原因导致的)认为Leader死了,但其实Leader还存活着。
  • 脑裂:由于假死会发起新的Leader选举,选举出一个新的Leader,但旧的Leader网络又通了,导致出现了两个Leader ,有的客户端连接到老的Leader,而有的客户端则连接到新的Leader。

ZooKeeper脑裂是什么原因导致的?

主要原因是 ZooKeeper 集群和 ZooKeeper client 判断超时并不能做到完全同步,也就是说可能一前一后,如果是集群先于 client 发现,那就会出现上面的情况。同时,在发现并切换后通知各个客户端也有先后快慢。一般出现这种情况的几率很小,需要Leader节点与 ZooKeeper 集群网络断开,但是与其他集群角色之间的网络没有问题,还要满足上面那些情况,但是一旦出现就会引起很严重的后果,数据不一致。

ZooKeeper 是如何解决“脑裂”问题的?

要解决 Split-Brain 脑裂的问题,一般有下面几种方法:

  • Quorums(法定人数)方式:比如3个节点的集群,Quorums = 2,也就是说集群可以容忍1个节点失效,这时候还能选举出1个lead,集群还可用。比如4个节点的集群,它的Quorums = 3,Quorums要超过3,相当于集群的容忍度还是1,如果2个节点失效,那么整个集群还是无效的。这是ZooKeeper防止“脑裂”默认采用的方法。
  • 采用Redundant communications(冗余通信)方式:集群中采用多种通信方式,防止一种通信方式失效导致集群中的节点无法通信。
  • Fencing(共享资源)方式:比如能看到共享资源就表示在集群中,能够获得共享资源的锁的就是Leader,看不到共享资源的,就不在集群中。
  • 仲裁机制方式。
  • 启动磁盘锁定方式。

要想避免ZooKeeper“脑裂”情况其实也很简单,在follower节点切换的时候不在检查到老的Leader节点出现问题后马上切换,而是在休眠一段足够的时间,确保老的Leader已经获知变更并且做了相关的shutdown清理工作了然后再注册成为Master就能避免这类问题了,这个休眠时间一般定义为与ZooKeeper定义的超时时间就够了,但是这段时间内系统可能是不可用的,但是相对于数据不一致的后果来说还是值得的。

ZooKeeper 默认采用了 Quorums 这种方式来防止“脑裂”现象。即只有集群中超过半数节点投票才能选举出 Leader。这样的方式可以确保 Leader 的唯一性,要么选出唯一的一个 Leader,要么选举失败。在 ZooKeeper 中 Quorums 作用如下:

  • 集群中最少的节点数用来选举Leader保证集群可用。
  • 通知客户端数据已经安全保存前集群中最少数量的节点数已经保存了该数据。一旦这些节点保存了该数据,客户端将被通知已经安全保存了,可以继续其他任务。而集群中剩余的节点将会最终也保存了该数据。

假设某个 Leader 假死,其余的 followers 选举出了一个新的 Leader。这时,旧的Leader 复活并且仍然认为自己是 Leader,这个时候它向其他 followers 发出写请求也是会被拒绝的。

因为每当新 Leader 产生时,会生成一个 epoch 标号(标识当前属于那个 Leader 的统治时期),这个 epoch 是递增的,followers 如果确认了新的 Leader 存在,知道其 epoch,就会拒绝 epoch 小于现任 leader epoch 的所有请求。

那有没有 follower 不知道新的 Leader 存在呢,有可能,但肯定不是大多数,否则新 Leader 无法产生。ZooKeeper 的写也遵循 quorum 机制,因此,得不到大多数支持的写是无效的,旧leader即使各种认为自己是 Leader,依然没有什么作用。

ZooKeeper 除了可以采用上面默认的 Quorums 方式来避免出现“脑裂”,还可以采用下面的预防措施:

  • 添加冗余的心跳线,例如双线条线,尽量减少“裂脑”发生机会。
  • 启用磁盘锁。正在服务一方锁住共享磁盘,“裂脑”发生时,让对方完全“抢不走”共享磁盘资源。但使用锁磁盘也会有一个不小的问题,如果占用共享盘的一方不主动“解锁”,另一方就永远得不到共享磁盘。现实中假如服务节点突然死机或崩溃,就不可能执行解锁命令。后备节点也就接管不了共享资源和应用服务。于是有人在HA中设计了“智能”锁。即正在服务的一方只在发现心跳线全部断开(察觉不到对端)时才启用磁盘锁。平时就不上锁了。
  • 设置仲裁机制。例如设置参考IP(如网关IP),当心跳线完全断开时,2个节点都各自ping一下 参考IP,不通则表明断点就出在本端,不仅”心跳”、还兼对外”服务”的本端网络链路断了,即使启动(或继续)应用服务也没有用了,那就主动放弃竞争,让能够ping通参考IP的一端去起服务。更保险一些,ping不通参考IP的一方干脆就自我重启,以彻底释放有可能还占用着的那些共享资源。

ZooKeeper集群“脑裂”的更多相关文章

  1. Zookeeper集群"脑裂"问题 - 运维总结

    关于集群中的"脑裂"问题,之前已经在这里详细介绍过,下面重点说下Zookeeper脑裂问题的处理办法.ooKeeper是用来协调(同步)分布式进程的服务,提供了一个简单高性能的协调 ...

  2. Elasticsearch部分节点不能发现集群(脑裂)问题处理

    **现象描述** es1,es2,es3三台es组成一个集群,集群状态正常, 当es1 服务器重启后,es1不能加到集群中,自己选举自己为master,这就产生了es集群中所谓的“脑裂” , 把es1 ...

  3. 为 Raft 引入 leader lease 机制解决集群脑裂时的 stale read 问题

    问题:当 raft group 发生脑裂的情况下,老的 raft leader 可能在一段时间内并不知道新的 leader 已经被选举出来,这时候客户端在老的 leader 上可能会读取出陈旧的数据( ...

  4. Elasticsearch之集群脑裂

    https://www.cnblogs.com/zlslch/p/6477312.html

  5. Zookeeper集群的"脑裂"问题处理 - 运维总结

    关于集群中的"脑裂"问题,之前已经在这里详细介绍过,下面重点说下Zookeeper脑裂问题的处理办法.ooKeeper是用来协调(同步)分布式进程的服务,提供了一个简单高性能的协调 ...

  6. ZooKeeper 03 - ZooKeeper集群的脑裂问题 (Split Brain问题)

    目录 1 ZooKeeper的主从机制 2 什么是ZooKeeper的脑裂 2.1 脑裂现象的表现 2.2 为什么会出现脑裂 3 ZooKeeper如何解决"脑裂" 3.1 3种可 ...

  7. ZooKeeper 02 - ZooKeeper集群的节点为什么是奇数个

    目录 1 关于节点个数的说明 2 ZooKeeper集群的容错数 3 ZooKeeper集群可用的标准 4 为什么不能是偶数个节点 4.1 防止由脑裂造成的集群不可用 4.2 奇数个节点更省资源 4. ...

  8. ZooKeeper集群与Leader选举

    说说你对ZooKeeper集群与Leader选举的理解?   ZooKeeper是一个开源分布式协调服务.分布式数据一致性解决方案.可基于ZooKeeper实现命名服务.集群管理.Master选举.分 ...

  9. Zookeeper集群为什么要是单数

    (原) 在zookeeper集群中,会有三种角色,leader. follower. observer分别对应着总统.议员.观察者. 半数以上投票通过:可以这样理解.客户端的增删改操作无论访问到了哪台 ...

随机推荐

  1. CSDN-markdown编辑器使用方法

    这里写自定义目录标题 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一 ...

  2. PyQt学习随笔:QTableWidget的visualRow、visualColumn、logicalRow、logicalColumn(可见行、逻辑行、可见列、逻辑列)相关概念及方法探究

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概念 关于逻辑行logicalRow.列logicalColumn和可见行visualRow.列 ...

  3. Get请求Test

    一.新建测试套 作为管理接口,可按功能分类,也可按业务逻辑分类,根目录下最多一级子目录.运行接口时,可按测试套为单位,整体运行. 二.选择请求类型,输入接口地址 根据接口文档中提供的接口请求类型及地址 ...

  4. Kubernetes 教程:在 Containerd 容器中使用 GPU

    原文链接:https://fuckcloudnative.io/posts/add-nvidia-gpu-support-to-k8s-with-containerd/ 前两天闹得沸沸扬扬的事件不知道 ...

  5. 关于VS.Net应用的图标提取方法

    .Net的资源文件 VS.Net 支持三种文件类型的resource:.txt..resx..resources. system.resources 名字空间支持三种资源文件: txt 文件,只能有字 ...

  6. Aizu2970 Permutation Sort

    题目大意 给你两个 \(n\) 个整数的排列,第一个排列表示原排列,第二个排列表示第 \(i\) 个数可以和i变成第 \(g_i\) 个数,问,最少对所有数进行几次操作可以使原排列变为有序的排列. 题 ...

  7. Java JVM——2.类加载器子系统

    概述 类加载器子系统在Java JVM中的位置 类加载器子系统的具体实现 类加载器子系统的作用 ① 负责从文件系统或者网络中加载.class文件,Class 文件在文件开头有特定的文件标识. ② Cl ...

  8. vue 图片优化

    https://developer.aliyun.com/mirror/npm/package/image-conversionnpm i image-conversion --save # or y ...

  9. pandas的学习3-设置值

    import pandas as pd import numpy as np # 我们可以根据自己的需求, 用 pandas 进行更改数据里面的值, 或者加上一些空的,或者有数值的列. # 首先建立了 ...

  10. 企业微信JS-SDK实现会话聊天功能

    vue引入企业微信JS-SDK实现会话聊天功能 这两天在做一个对接企业微信实现会话聊天的功能, 发现企业微信文档这块儿做的不是特别详细,网上搜索也没找到特别完整的流程. 期间也踩了不少的坑, 在此进行 ...