-
概述
Zookeeper 是一个分布式的,开放源码的分布式应用程序协调服务。
提供的服务:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡
-
特点
Zookeeper:一个领导者(Leader),多个跟随者(Follwer)组成的集群。
集群中只要有半数以上节点存活,zookeeper集群就能正常服务。所以zookeeper适合安装奇数台服务器
全局数据一致:每个Server保存一份相同的数据副本,Client无论连接哪个Server,数据都是一致的
更新请求顺序执行,来自同一个Client的更新请求按其发送顺序依次执行。
数据更新原子性,一次数据更新要么成功,要么失败。
实时性,在一定时间范围内,Client能读到最新数据。
-
数据结构
与Unix文件系统类似,整体上是一棵树,每个节点称为ZNode,每个ZNode默认能存储1MB的数据,每个ZNode可以通过其路径唯一标识。
-
应用场景
统一命名服务:分布式环境下,经常需要对应用/服务进行统一命名,便于识别。 IP不容易记住,域名容易记住。
统一配置管理:所有节点的配置信息是一致的。配置文件修改能够快速同步到各个节点上,
统一集群管理:实时掌握每个节点的状态,根据节点实时状态做出一些调整。
服务器节点动态上下线:客户端实时洞察服务器上下线变化。
软负载均衡:记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求。
-
配置参数解读
tickTime = 2000:通信心跳时间,zookeeper服务器与客户端心跳时间,单位毫秒
initLimit = 10 :LF初始通信时限,Leader和Follower初始连接时能容忍的最多心跳数(tickTime的数量)
syncLimit = 5 :LF同步通信时限,Leader和Follower之间通信时间如果超过syncLimit * tickTime,默认Follower 死掉,并进行删除。
dataDir:保存zookeeper中的数据,默认的tmp目录,会被Linux系统定期删除。
clientPort = 2181:客户端连接端口(通信端口)。
-
选举机制
客户端每次写操作都有事务id(zxid)
SID:服务器ID。用来唯一标识一台zookeeper集群中的机器,每台服务器不能重复,和myid一致。
ZXID:事务ID。ZXID用来标识一次服务器状态的变更。
Epoch:每个Lader任期的代号。没有Lader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加。
-
第一次启动
① 服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票,不够半数以上,选举无法完成,服务器1状态为LOOKING;
②服务器2启动,再次发起选举,服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服务器2的myid比自己目前投票选举的大,更改为推举服务器二。此时服务器1为0票,服务器2为2票,没有半数,无法完成选举,服务器1、2状态为LOOKING;
③服务器3启动,再次发起选举。服务器1、2都会更改选票为服务器3,因为自身的myid比服务器3的小。此时服务器3的票数已经超过半数,服务器3当选Leader,服务器1、2状态更改为FOLLOWING,变成Follower,服务器3更改状态为LEADING;
④服务器4启动,再次发起选举,但Loader已经选出,所以不会更改选票信息,此时少数服从多数,更改自身的状态为FOLLOWING,成为Follower。
⑤服务器5启动,和④一样的流程。
-
非第一次启动
①当zookeeper集群中的一台服务器出现以下两种情况之一时,就会开始进入Loader选举:
-
服务器初始化启动
-
服务器运行期间无法和Leader保持连接
-
集群中本来就已经存在一个Leader,其他的机器试图进行选举,会被告知当前服务器Leader信息,并且和Leader建立连接,进行状态同步。
-
集群中确实不存在Leader
选举Leader规则:①EPOCH大的直接胜出 ②EPOCH相同,事务id大的胜出 ③事务id相同,服务器id大的胜出
-
-
-
客户端命令行操作
命令行语法
命令基本语法 功能描述 help 显示所有操作命令 Is path 使用 Is 命令来查看当前znode的子节点[可监听] -w 监听子节点变化 -s 附加次级信息 create 普通创建 -s 含有序列 -e 临时(重启或者超时消失) get path 获得节点的值[可监听] -w 监听节点内容变化 -s 附加次级信息 set 设置节点的具体值 stat 查看节点状态 delete 删除节点 deleteall 递归删除节点 czxid:创建节点的事务zxid
每次修改zookeeper状态都会产生一个zookeeper事务ID。事务ID是zookeeper中所有修改总的次序,每次修改都有唯一的zxid。
ctime:znode被创建的毫秒数(从1970年开始)
mzxid:znode最后更新的事务zxid
mtime:znode最后修改的毫秒数(从1970年开始)
pZxid:znode最后更新的子节点zxid
cversion:znode子节点变化号,znode子节点修改次数
dataversion:znode数据变化号
aclVersion:znode访问控制列表的变化号
ephemeralOwner:如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。
dataLength:znode的数据长度
numChildren:znode子节点数量
-
节点类型(持久/短暂/有序号/无序号)
创建Znode时设置顺序标识,Znode名称后附加一个值,顺序号是一个单调递增的计数器,由父节点维护。顺序号可以被用于为所有的事件进行全局排序,客户端可以通过顺序号推断事件的顺序。
持久(Persistent):客户端和服务器端断开连接后,创建的节点不删除
短暂(Ephemeral):客户端和服务器端断开连接后,创建的节点自己删除
① 持久化目录节点:客户端与zookeeper断开连接后,该节点依旧存在
② 持久化顺序编号目录节点:客户端与zookeeper断开连接后,该节点依旧存在,只是zookeeper给该节点名称进行顺序编号。
③ 临时目录节点:客户端与zookeeper断开连接后,该节点删除
④ 临时顺序编号目录节点:客户端与zookeeper断开连接后,该节点被删除,只是zookeeper给该节点名称进行顺序编号。
-
监听器
1) 首先要有一个main()线程
2) 在main线程中创建zookeeper客户端,这是就会创建两个线程,一个负责网络连接通信(connet),一个负责监听(listener)
3) 通过connect线程将注册的监听事件添加到列表中
4) 在zookeeper的注册监听器列表中将注册的监听事件添加到列表中
5) zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程
6) listener线程内部调用了process()方法
常见的监听
1) 监听节点数据的变化 get path [watch]
2) 监听子节点增减的变化 Is path [watch]
-
zookeeper分布式锁案例
create -e -s /locks/seq- 创建临时顺序节点
1) 接收到请求后,在/locks节点下创建一个临时顺序带序号的节点 -- 获取到锁处理业务
2) 判断自己是不是当前节点下最小的节点:是,获取到锁;不是,对前一个节点进行监听
3) 获取到锁,处理完业务后,delete节点释放锁,然后下面的节点将收到通知,重复第二步判断
-
Curator 框架
1)原生的Java API 开发存在问题:
(1)会话连接是异步的,需要自己去处理。比如使用CountDownLatch
(2)Watch需要重复注册,不然就不能生效
(3)开发的复杂性还是比较高
(4)不支持多节点删除和创建。需要自己去递归
2)Curator是一个专门解决分布式锁的框架,解决原生Java API分布式的问题。
-