ZooKeeper分布式锁!

方案一:

ZK中节点中存放一个标识,线程获得锁时,先检查该标识是否是无锁标识,若是可修改为占用标识,使用完再恢复为无锁标识。

方案二:

使用子节点,每当有线程来请求锁的时候,便在锁的节点下创建一个子节点。

  • 子节点类型必须维护一个顺序,对子节点的自增序号进行排序。

默认总是最小的子节点对应的线程获得锁,释放锁时删除对应子节点便可。

image-20231020112215442

死锁风险:

方案一:

要是持有锁的线程发生了意外,释放锁的代码无法执行,锁就无法释放,其他线程就会一直等待锁,相关同步代码便无法执行。

方案二:

可以利用ZK的临时顺序节点来解决这个问题,只要线程发生了异常导致程序中断,就会丢失与ZK的连接,ZK检测到该链接断开。

就会自动删除该链接创建的临时节点,这样就可以达到即使占用锁的线程程序发生意外,也能保证锁正常释放的目的。

避免羊群效应:

把锁请求者按照后缀数字进行排队,后缀数字小的锁请求者先获取锁。

如果所有的锁请求者都 watch 锁持有者,当代表锁请求者的 znode 被删除以后,所有的锁请求者都会通知到。

  • 但是只有一个锁请求者能拿到锁,这就是羊群效应。

为了避免羊群效应,每个锁请求者 watch 它前面的锁请求者。

  • 每次锁被释放,只会有一个锁请求者 会被通知到。

  • 这样做还让锁的分配具有公平性,锁定的分配遵循先到先得的原则。

image-20231020112611783

用 ZooKeeper 实现分布式锁的算法流程,根节点为 /lock

客户端连接 ZooKeeper,并在/lock下创建临时有序子节点。

  • 第一个客户端对应的子节点为/lock/lock01/00000001,第二个为 /lock/lock01/00000002

其他客户端获取/lock01下的子节点列表,判断自己创建的子节点是否为当前列表中序号最小的子节点。

如果是则认为获得锁,执行业务代码,否则通过 watch 事件监听/lock01的子节点变更消息。

  • 获得变更通知后重复此步骤直至获得锁。

完成业务流程后,删除对应的子节点,释放分布式锁。