zookeeper:集群中实例的数量

java服务器

浏览数:697

2018-12-8

概览

应用程序通过zookeeper客户端连接zookeeper。
客户端可以是zookeeper自身携带的客户端(zookeeper把client代码跟server放在一起,这点很多人有非议)
也可以是一些其它的开源客户端例如apache curator和zkClient。
图片描述

zookeeper可以有两种部署模式,一种是单机版,一种是集群版。
所谓单机版,亦即只有一个zookeeper实例。
集群版会有多个zookeeper实例,多个实例之间会有一个master,他们之间的状态信息也会进行复制。

集群的数目:奇数

从各种书或网上的资料上经常能看到一个建议:

zookeeper的集群里,实例的个数最好是奇数

对于客户端来说,它并不需要关心zookeeper集群中有多少个实例,实例之间是怎么协商的。
如果客户端新建了一个znode,并得到了集群的响应,那么客户端就可以认为集群已经替它保存好了这个znode。
而实际上,在zookeeper集群内部,它们并不会把znode的创建都通知到每个zookeeper实例后才返回响应消息给客户端。

可以看出来,在zookeeper集群中,有一件非常重要的事经常会发生,那就是如何让集群中的每个实例对某个公共状态的变化达成共识。
公共状态的变化包括什么呢?

  • leader选举

  • 来自客户端的各种updates

zookeeper采用的方式是,如果集群中有过半数的实例同意某个公共状态的变化,那么便认为集群最终会对这个公共状态达成一致意见。

为什么是过半数?

我们先看看如果不是“过半数”,会发生什么情况:
假设现在集群中有5个实例,当客户端新建一个znode “/test”后,只有2个实例同步了这个新建的znode,并通知客户端znode创建成功了。
在这2个实例通知其它3个实例之前,与其他3个实例发生了网络隔离(俗称“脑裂”,split-brain),变成了两个小集群了。
有2个实例拥有/test这个znode,而另外3个没有/test这个znode,并且在网络隔离还没恢复之前,这3个没有znode的实例永远不会得到关于/test这个znode被创建的通知。
当有一个客户端碰巧连接到的是那3个实例中的其中一个时,客户端永远也看不到/test这个znode。
这就出现了数据不一致的情况了。
如果是“过半数”,那情况就不一样了:
还是假设集群中有5个实例,这个时候,必须得有至少3个实例同步了/test这个znode后,客户端才会得到响应。
再次考虑网络隔离发生的场景,在这个3个实例还没通知到另外2个实例之前,又被隔离成两个小集群。
假设客户端连接到的是2个实例的那个集群,由于zookeeper认为要至少3个实例(过半数)存活才能提供服务,所以客户端获取不成功,数据是一致的。

只要是采取“过半数”的策略,无论网络怎么隔离,无论脑怎么裂,能够提供服务(意味着有过半数的实例)的那个小集群里,至少有一个实例是同步到最新的状态信息的。

包括zookeeper本身的leader选举,以及对znode的更新操作,都需要“过半数“这个作为基本方针。

为什么是奇数

偶数也可以有过半数,例如,4个实例的集群,3就是一个过半数
但为什么还是奇数最好?
如果集群有5个实例,那么只能容忍2个实例的崩溃。
如果集群中有6个实例,同样也只能容忍2个实例的崩溃。
在相同的容忍度下,6个和5个有什么区别:

  • 由于集群间需要互相通信,实例越多,网络开销越大

  • 实例越多,5个实例的时候,发生3个实例崩溃的概率要小于6个实例的时候。

那我们为什么不比较6个和7个?
没有可比性,容忍度不同。

如果有3,5,7供选择,该怎么选?
集群中的实例数目越多,就越稳定。
但实例数目越多,网络开销以及实例之间协调的耗费也会比较大。

这只是一个权衡利弊取其轻的原则

原文地址:https://segmentfault.com/a/1190000005643792