zookeeper分布式锁
zookeeper的节点有四种节点:
Persist vs. Ephemeral
- Persist节点,一旦被创建,便不会意外丢失,即使服务器全部重启也依然存在。每个 Persist 节点即可包含数据,也可包含子节点
- Ephemeral节点,在创建它的客户端与服务器间的 Session 结束时自动被删除。服务器重启会导致 Session 结束,因此
Ephemeral 类型的 znode 此时也会自动删除
Sequence vs. Non-sequence
Non-sequence节点,多个客户端同时创建同一 Non-sequence节点时,只有一个可创建成功,其它匀失败。并且创建出的节点名称与创建时指定的节点名完全一样
- Sequence节点,创建出的节点名在指定的名称之后带有10位10进制数的序号。多个客户端创建同一名称的节点时,都能创建成功,只是序号不同
此外,zookeeper有watch机制,可以监听到数据的变化从而触发watch,watch有以下特点:
主动推送 Watch被触发时,由 Zookeeper 服务器主动将更新推送给客户端,而不需要客户端轮询。
一次性 数据变化时,Watch 只会被触发一次。如果客户端想得到后续更新的通知,必须要在 Watch 被触发后重新注册一个Watch。
可见性 如果一个客户端在读请求中附带 Watch,Watch 被触发的同时再次读取数据,客户端在得到 Watch消息之前肯定不可能看到更新后的数据。换句话说,更新通知先于更新结果。
顺序性 如果多个更新触发了多个 Watch ,那 Watch 被触发的顺序与更新顺序一致。
基于zookeeper的分布式锁有两种实现方式:公平模式和非公平模式,可以利用zookeeper的节点的这几种特性来实现不同的分布式锁
一.非公平模式:
竞争锁:
非公平模式的zookeeper的分布式锁使用的是Non-sequence+Ephemeral节点实现的,此节点的实现方式和Redis实现分布式锁的实现方式比较类似.
zookeeper由于Non-sequence节点的特性,在创建节点时,多个节点只会创建一个成功,这个节点就是主节点,其余的节点就是follower,这样就保证了只有一个线程能够拿到锁
释放锁:
由于Ephemeral节点的存在,锁的获得者应该能够正确释放已经获得的锁,并且当获得锁的进程宕机时,锁应该自动释放,从而使得其它竞争方可以获得该锁,从而避免出现死锁的状态
或者leader主动释放锁,并且当领导所在进程宕机时,领导权应该自动释放,从而使得其它参与者可重新竞争领导而避免进入无主状态
感知锁的释放:
感知锁的释放主要是watch机制的存在,在leader释放锁时,节点删除,其他线程会感知到锁的释放,从而竞争锁
总结:
非公平模式实现简单,每一轮选举方法都完全一样 竞争参与方不多的情况下,效率高。每个 Follower 通过 Watch 感知到节点被删除的时间不完全一样,只要有一个 Follower 得到通知即发起竞选,即可保证当时有新的 Leader 被选出 给Zookeeper 集群造成的负载大,因此扩展性差。如果有上万个客户端都参与竞选,意味着同时会有上万个写请求发送给 Zookeper。如《Zookeeper架构》一文所述,Zookeeper 存在单点写的问题,写性能不高。同时一旦 Leader 放弃领导权,Zookeeper 需要同时通知上万个 Follower,负载较大。
二.公平模式:
竞争锁:
公平锁主要依据的是zookeeper的Sequence+Ephemeral节点的特性实现的 在线程启动时会根据线程进行编号,由于Sequence节点的特性,每个线程均能成功创建出节点,此处节点的选举有些类似于zookeeper的选举,在启动时会根据节点的编号顺序来指定主节点,例如有三个节点,编号分别为1,2,3,此时会指定最小的节点为leader,其余的节点为follower,同时此节点对应的线程watch是比自己节点小的节点,也就是说3线程watch2节点,2线程watch1节点
释放锁:
Leader 如果主动放弃领导权,直接删除其创建的节点即可. 如果 Leader 所在进程意外宕机,其与 Zookeeper 间的 Session 结束,由于其创建的节点为Ephemeral类型,故该节点自动被删除.
感知锁的释放:
与非公平模式不同,每个 Follower 并非都 Watch 由 Leader 创建出来的节点,而是 Watch 序号刚好比自己序号小的节点,所以主节点释放后刚好比主节点序号大的节点就会感知到,比如:1节点释放后2线程会watch到1节点释放锁从而竞争锁,但是在竞争锁之前会判断此节点是否是最小的节点,如果不是仍然不会成为主节点.(1节点释放锁之前2节点宕机的情况下,3线程会watch到2节点的释放,此时3线程会判断3节点是否是最小的节点,由于此时1节点没有删除,所以3节点不会成为leader,并且3线程会watch比2节点小的节点也就是1节点)
总结:
实现相对复杂 扩展性好,每个客户端都只 Watch 一个节点且每次节点被删除只须通知一个客户端 旧 Leader 放弃领导权时,其它客户端根据竞选的先后顺序(也即节点序号)成为新 Leader,这也是公平模式的由来 延迟相对非公平模式要高,因为它必须等待特定节点得到通知才能选出新的 Leader