zk的数据结构
zookeeper的数据模型和分布是文件系统类似,是一种层次化的属性结构,如下图,和文件系统不同的是,zk的数据是结构化存储的,并没有在物理上体现出文件和目录。
zk树种的每个节点被称为Znode,Znode上维护了一个Stat状态信息,包含了数据变化的时间和版本等,每个Znode上可以设置一个value值,zk不用于大容量对象的存储,他只是管理和协调有关的数据,所以value的数据大小不建议设置的很大,会带来较大的网络开销。
zk上每个节点上的数据都是可以读写的,都是指修改value上的数据。节点的创建必须按层级创建。
Znode的特性
Znode按类型分为
持久化节点
临时节点:节点的生命周期和创建该节点的客户端生命周期一致,一旦该客户端的会话结束,客户端创建的节点自动删除。
顺序节点:在创建节点的后面会增加一个递增序列,该序列同级父节点下是唯一的。
容器节点:容器节点下最后一个节点删除,则容器节点自动删除。
TTL节点:针对持久化节点和持久化有序节点,设置一个存活时间,在存活时间内,该节点没有变化并且没有任何子节点就会自动删除。
zk的Watcher机制
zk提供了一种对znode的订阅、通知机制,就是当Znode的数据放生变化时或者zk的客户端连接状态发生变化时,会触发事件的通知。这个机制在服务注册发现时,针对服务的调用者及时感知到服务的提供者的变化提供了非常好的解决方案。
ZK的java API提供了三种机制对Znode中的数据进行监听
getData()
获取指定节点的value信息,并且可以注册监听 ,当监听的节点进行创建、修改、删除时,会触发相应的事件通知。
getChildern()
用于获取指定节点的所有子节点,并且允许注册监听,当监听节点的子节点进行创建、修改、删除时,会触发相应的事件通知。
exists()
用于判断指定节点是否存在,同样可以进行注册监听,监听时间类型和getData相同
Watcher事件的触发都是一次性的,为了解决这个问题可以在收到事件的回调中再次注册事件
zk分布式锁原理
使用zk的分布式锁需要达到排他性的目的需要使用节点的两个特性:临时节点,以及同级节点之间的唯一性
获得锁:
在获得排它锁时,所有客户端去ZK服务器上/Exclusive_Locks节点下创建一个临时节点/lock。ZK基于同级节点的唯一性会保所有客户端只有一个客户端能创建成功,创建成功的客户端获得排它锁,没有获得锁的客户端就需要通过Waatcher机制监听/Exclusive_Locks节点下子节点的变更事件,用于实时监控/lock节点的变化情况作出反应。
释放锁:
在获得锁的过程中,我们定义的锁节点/lock为临时节点,在以下两种情况下会释放锁
1.获得锁的客户端因为异常断开了和服务端的链接,基于临时节点的特性,/lock节点会自动删除
2.获得锁的客户端相应的逻辑执行完成,主动删除了/lock节点,
当/lock节点被删除后,Zookeeper会通知所有监听了/Exclustive_Locks子节点变化的客户端,这些客户端再次发起创建/lock节点的操作来获得排它锁。
Zookeeper的master选举
zk有两种方式来实现master选举
1.基于同级节点下不能重复创建一个已经存在的节点
假设集群中有三个节点,需要选举出master,那么这三个节点会同时Zookeeper服务器上创建临时节点/master_election,由于节点的特性只会有一个客户端创建成功,创建成功的客户端所在的节点就是master,没有创建成功的客户端就会针对该节点注册Watcher事件,用于监控master机器是否存活,一旦master挂了,也就是/master_election节点被删除,那么其他的客户端就=就会重新发起master的选举。
2.利用临时有序节点的特性
所有参与选举的客户端在Zookeeper服务器上的/master节点下创建一个临时有序节点,编号最小就是master节点,后续的节点可以监听前一个节点的删除事件,用于处触发重新选举。