ZooKeeper集群脑裂问题处理,值得收藏!

本文重点讲解ZooKeeper脑裂问题的处理办法。ZooKeeper是用来协调(同步)分布式进程的服务,提供了一个简单高性能的协调内核,用户能够在此之上构建更多复杂的分布式协调功能。脑裂一般会出如今集群环境中,好比Elasticsearch、ZooKeeper集群,而这些集群环境有一个统一的特色,就是它们有一个大脑,好比Elasticsearch集群中有Master节点,ZooKeeper集群中有Leader节点。程序员

先分享一个ZooKeeper知识点思惟导图给你们面试

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->一、4->一、5->二、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的一方干脆就自我重启,以完全释放有可能还占用着的那些共享资源。

总结

总结了2020面试题,这份面试题的包含的模块分为19个模块,分别是: Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM 。

关注公众号:程序员白楠楠,获取上述资料。

 
相关文章
相关标签/搜索