ZooKeeper服务端可以支持单节点和集群,对于单节点模式,所有客户端都连接到同一个服务端节点执行操作;对于集群模式,ZooKeeper服务端会选举一个leader节点,其它服务端节点与leader相连,并保存相同的数据。每个服务端节点都能处理读操作,但对于写操作,都需要通过leader节点发起。

服务端节点管理

在集群模式下,ZooKeeper服务端包含以下3种类型的服务端节点:
-leader:由集群中的所有服务端节点共同选举产生,负责保障修改操作(包括create、setData和delete)的顺序;
-follower:leader之外的节点,参与leader的选举过程,并参与修改操作的投票;
-observer:不参与leader选举过程和修改操作的投票,仅保存数据,响应数据修改,为客户端提供查询操作。
leader、follower和observer都保存完整的数据。由于observer节点不参与leader的选举和修改操作的投票,增加observer节点可以提高系统的吞吐量,却不会对集群维护的效率造成太多影响。

数据查询

ZooKeeper的查询操作包括exists、getData和getChildren。对于查询操作,ZooKeeper服务端直接从本地读取数据并返回给客户端。由于该特性,ZooKeeper对于读操作是非常快速的,并且很容易通过扩展服务器数量来增加吞吐量。

数据修改

ZooKeeper的改变操作包括create、delete和setData,所有改变操作都由leader完成。具体过程如下。
ZooKeeper客户端发起一个修改请求,包含修改的znode的新数据和新的版本号,新的版本号是在现有的版本号基础上加1,ZooKeeper服务端收到修改请求后会将请求转发给leader节点,leader节点将修改请求封装为一个事务,并发起修改流程(详细流程参考后续的“数据一致性”章节)。
修改操作是原子的,当ZooKeeper的服务端修改数据时,需要确保所有的改变(数据和版本号)作为一个事务修改,要么都成功,要么都失败。ZooKeeper没有提供像关系数据库那样的回滚机制,但他保证多个事务之间不会有相互影响。
事务也要求是等幂的,即同一个事务执行多次,会得到同样的结果。甚至,同一批事务执行多次,只要都按照相同的顺序执行,都能得到相同的结果。
ZooKeeper为每个事务生成一个唯一标识zxid(ZooKeeper transaction ID),zxid用于保障ZooKeeper的服务端按序执行事务。zxid分为epoch和counter两个部分,各占32位,epoch用于标识目前是哪个leader,而counter标识当前leader处理的事务的流水号。当新的leader产生时,epoch加1,以帮助在服务器之间同步状态。每处理一个新增的事务counter加1,因此如果两个事务的zxid为zxid1、zxid2,如果zxid1大于zxid2,则zxid1在zxid2后面产生。

leader选举

Zookeeper中leader的作用是对客户端的create、setData和delete操作排序。leader由集群中节点一起选举产生。

服务端节点状态管理

ZooKeeper的服务端节点存在以下状态:

 - LOOKING:正在寻找一个已经存在的leader,或者进行leader的选择;
 - LEADING:赢得了leader的选举,成为leader;
 - FOLLOWING:在leader选举中失败,成为follower。

Split-Brain

Split-Brain问题是当网络发生故障时,集群被分成了两个部分,两个部分都不知道对方是否还在正常运行,所以这两个部分都会选举1个leader,而一旦两部分都选出了leader,而网络又恢复了,整个集群中就会出现两个leader,导致集群的状态不一致。
ZooKeeper为了解决Split-Brain问题,要求集群中至少要有超过一半的节点(不包含oberver)正常运行整个集群才能正常运转,这也是集群节点数量为奇数容忍度高的原因。因为:集群节点数量为3,容许1个节点失效;集群节点数量为4,容许1个节点失效,节点数量为4没有增加容忍失效的节点数量,反而增加了数据同步的负担(增加吞吐量可以通过增加observer节点来实现)。

leader选举过程

ZooKeeper服务端节点启动后就进入LOOKING状态,如果集群中已经存在一个leader,则其它节点会通知该节点leader是谁,当该节点连接到leader后,开始与leader做数据同步,确保自己的状态和leader的状态一致。
如果集群中不存在leader,该节点则需要和集群中的所有其它节点一起选出一个leader,被选出的leader进入LEADING状态,未成为leader的选举节点进入FOLLOWING状态。选举过程如下:
当一个节点进入LOOKING状态,它向集群中的其他所有节点发送一个通知消息,消息中包含它的当前投票对象的服务器标识(sid)和投票对象最后执行的事务的zxid。
当一个节点收到一个投票通知后,根据以下规则处理:

  1. 令voteId和voteZxid为收到通知消息的sid和zxid;
  2. 如果(voteZxid > myZxid) 或者 (voteZxid = myZxid and voteId > mySid),保留当前投票;
  3. 否则,将自身sid和zxid作为voteId和voteZxid。

一旦一个节点收到大多数节点(超过一半)的相同投票,节点就宣布leader选举完成。如果leader是他自身,他则开始执行leader角色,否则他成为follower并尝试连接到leader。一旦连接到leader,则开始和leader同步状态,只有当状态同步完成后,follower才开始处理新的请求。如果连接失败,该server将不可用。
ZooKeeper的选举过程决定了集群中只要有一半以上的节点可用,集群就能正常运转。当集群的leader挂掉之后,只要集群还有一半以上的节点可用,集群就可以选取出新的leader,继续正常运转。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐