简单点来说,脑裂(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定义的超时时间就够了,但是这段时间内系统可能是不可用的,但是相对于数据不一致的后果来说还是值得的。