zookeeper

前言

在现在分布式、微服务大行其道的今天,肯定都会接触ZooKeeper这个框架。本人也只是在Dubbo的项目中有使用过(当然Kafka的集群部署也是基于ZooKeeper,这个就不算使用了)。但是它可以做的事远不止在Dubbo中的使用。所以先了解了ZooKeeper的基本模型、概念以及使用,以便加深学习

zookeeper是什么

ZooKeeper 是一个开源的分布式协调服务,设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。

ZooKeeper 是一个典型的分布式数据一致性解决方案。其主要应用场景:

  1. master节点选举,主节点挂了以后,从节点就会接收工作,并且节点是唯一的。保证高可用(比如Kafka集群)
  2. 统一配置文件管理,即只需要部署一台服务器,则可以把相同的配置文件同步更新到其他服务器。
  3. 发布与订阅。类似于消息队列MQ,dubbo发布者把数据发布到znode上,订阅者会拉出数据
  4. 分布式锁
  5. 集群管理,集群中保证数据的强一致性(Dubbo的注册中心)

本人主要接触ZooKeeper还是在公司使用Dubbo的时候接触的,所以ZooKeeper主要是用于:服务的容错、负载均衡、查找服务和管理服务,还可以选择其作为配置中心的集中式管理。

ZooKeeper的数据模型

Session机制

在了解ZooKeeper的数据模型之前,有必要了解ZooKeeper的Session机制

Session是指客户端和服务端之间会话。在ZooKeeper中是指一个客户端与ZooKeeper之间的一次长连接。客户端在启动时会与服务端建立一个Tcp连接。后续客户端可已通过这个连接发送消息给服务端,也可以监听服务端发送来的消息。客户端和服务端有一个心跳机制来维持和判断这个连接的有效性,可以通过Session中的sessionTimeout值来设置一个客户端的超时时间。同时当由于服务器原因或者客户端主动要求端口连接的时候,只要在超时时间内重新连接任一一台ZooKeeper机器,那么之前创建的 Session仍然有效。

和我们平常接触到HttpSession一样,每次创建一次会话,服务端都会为该客户端分配一个SessionId,该SessionId也是全局唯一的。

数据模型

ZooKeeper的数据模型和我们经常使用的Unix/Linux的文件系统是类似的。

ZooKeepr的内存模型

上图中我们可以比较明显看到和文件系统很像,实际它提供的操作API都有点类似。正确情况下,我们会在会根目录下创建一个自己项目的Znode。比如dubbo、kafka以便隔离数据。和我们创建文件夹的思路一样

znode

  • 在ZooKeeper中每一个节点都称之为znode,它本身可以有子节点,也可以有数据。
  • 每一个节点分为临时节点和永久节点,临时节点在客户端断开后消失。也就是Session超时
  • 每一个节点都有各自的版本号。可以通过命令行来显示节点信息
  • 没当节点的数据发生改变,那么该节点的版本号都会累加(乐观锁)
  • 删除、修改节点时,如果版本号不匹配会报错(乐观锁)
  • 由于ZooKeeper的数据都存在内存中,每个节点的数据不建议存储过大的数据。几K即可
  • 节点也可以设置acl权限。可以通过权限来限制用户的操作(unix的文件权限)

这里需要注意znode分为临时节点和永久节点,临时节点在session关闭时会自动删除

watch机制

ZooKeeper在针对每个节点的操作,都会有监督者(watcher),当监控对象znode发生了变化,则触发watcher事件,可以理解为监听器。zk中的watcher是一次性的,触发后立即销毁

父节点、子节点 增删改都能够触发watcher事件。 具体的watcher事件

  1. 创建节点触发, NodeCreated
  2. 修改节点触发,NodeDataChanged
  3. 删除节点触发,NodeDeleted
  4. 增加子节点触发,NodeChildrenChanged
  5. 删除子节点触发,NodeChildrenChanged
  6. 修改子节点不触发监听

这里我建议自己在命令行或者用代码api来自己体验下

ZooKeeper命令行

了解基本的命令行,其实ZooKeeper的命令行并不多,这里只做简单介绍

执行./zkCli.sh即可打开命令行

查询命令

ls path [watch] 查询目录

watch 设置子节点的watch事件

stat path [watch] 查询详细信息

watch 设置当前节点的watch事件(下面的watch都是类似的机制不在说明)

节点信息如下:

  • cZxid 创建的id
  • ctime 创建的时间
  • mZxid 修改的id
  • mtime 修改的时间
  • pZxid 父节点的id
  • cversion 子节点的version
  • dataVersion 数据的version
  • aclVersion 权限的version
  • ephemeraOwner 0x0为永久节点,其他为临时节点(待定)
  • datalength 数据长度
  • numChildren 子节点的大小

zxid:ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid. 由于zxid的递增性质, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2发生
创建任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.

ls2 path [watch] 查询目录,同时展示节点的信息

get path [watch] 可以将目录的数据取出来

创建命令

create [-s] [-e] path data acl

-e 临时节点
-s 顺序节点 在创建文件夹会在后面自动添加1开始的顺序

create /test/name hello-world

修改命令

set path data [version]

version 主要用于更新时的乐观锁

删除命令

delete path [version]

version 主要用于删除时的乐观锁

ZooKeeper的集群

前面已经了解的ZooKeeper的概念,已经他能够为我们做什么。现在来了解一下ZooKeeper是怎么保证数据的统一性。以及自身集群的高可用。

ZooKeeper 中的角色

ZooKeeper在实际生产环境中是推荐使用集群方式。

在ZooKeeper的集群中引入了Leader、Follower、Observer三种角色。如下图所示

ZooKeeper集群

ZooKeeper集群角色表

ZooKeeper集群中的所有机器会通过Leader选举过程来选定一台成为Leader的机器。
该Leader既可以为客户端提供读服务和写服务,但是Follower和Observer都只能提供读服务。Follower和Observer的唯一区别就是不参与Leader的选举过程,Observer仅仅是用提升服务的读取速度而存在的。因为Follower的无限增多也会影响选举的性能。

ZooKeeper的核心是原子广播,这个机制保证各个Server之间的同步。实现这个机制的协议叫Zab协议。Zab协议有两种模式:恢复模式(选主)和广播模式(同步)

当Leader服务器出现崩溃,重启,网络中断等异常情况时,Zab协议会进入恢复模式并选举出新的Leader服务

大致步骤如下:

  1. Leader election(选举阶段):节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
  2. Discovery(发现阶段):在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
  3. Synchronization(同步阶段):同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后 准 leader 才会成为真正的 leader。
  4. Broadcast(广播阶段) 到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。

这里通过第一条的规则,所以ZooKeeper部署推荐为单数服务,假如3台机器。最大允许宕机一台。而四台机器也是最大允许宕机一台。所以并不推荐部署双数机器部署

ZooKeeper的读写机制

  • Zookeeper是一个由多个server组成的集群
  • 一个leader,多个follower
  • 每个server保存一份数据副本
  • 全局数据一致
  • 分布式读写
  • 更新请求转发,由leader实施

Zookeeper的保证

  • 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
  • 数据更新原子性,一次数据更新要么成功,要么失败
  • 全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的
  • 实时性,在一定事件范围内,client能读到最新数据

Zookeeper的选举方式

首先选举必须要半数通过才行

简单模拟一下:

  • A提案说,我要选自己,B你同意吗?C你同意吗?B说,我同意选A;C说,我同意选A。(注意,这里超过半数了,其实在现实世界选举已经成功了。但是计算机世界是很严格,另外要理解算法,要继续模拟下去。)
  • 接着B提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;C说,A已经超半数同意当选,B提案无效。
  • 接着C提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;B说,A已经超半数同意当选,C的提案无效。
  • 选举已经产生了Leader,后面的都是follower,只能服从Leader的命令。而且这里还有个小细节,就是其实谁先启动谁当头。

Zab协议和Paxos算法

Paxos算法应该可以说是ZooKeeper的灵魂了。但是ZooKeeper并没有完全采用Paxos算法 ,而是使用ZAB协议作为其保证数据一致性的核心算法。另外,在ZooKeeper的官方文档中也指出,ZAB协议并不像Paxos算法那样,是一种通用的分布式一致性算法,它是一种特别为Zookeeper设计的崩溃可恢复的原子消息广播算法。

这里Paxos算法暂时没什么了解,可以参考一些文章和一些书籍了解

总结

  • ZooKeeper本身就是一个分布式程序。超过半数以上存活,ZooKeeper就能正常服务
  • ZooKeeper的数据保存在内中,保证低延迟和高吞吐量,也不建议在znode中保存过大的数据
  • ZooKeeper推荐使用在读多写少的场景写,上面我们可以到了ZooKeeper只有leader才能执行写操作,这样做确实天然的保证的其顺序性,但是也影响了性能
  • znode有临时节点和永久节点的区分,临时节点在Session关闭时删除。(Session的关闭时通过Session超时来决定的,如果断开后再超时时间连上来Session是会继续维持)
  • ZooKeeper对于客户端主要提供两个操作:数据的增删改查和数据的监听服务

参考