ZooKeeper 实现分布式锁的核心思路是利用节点的唯一性、有序性和监听机制,确保同一时间只有一个客户端持有锁,具体实现基于“临时顺序节点”,分 3 个关键步骤:
1. 核心依赖:ZooKeeper 的节点特性
实现分布式锁依赖其 2 个核心节点特性,是锁逻辑的基础:
临时节点(Ephemeral Node):客户端与 ZooKeeper 断开连接(如宕机、网络中断)时,临时节点会被自动删除,避免“死锁”(客户端宕机后锁无法释放)。
顺序节点(Sequential Node):创建节点时,ZooKeeper 会自动在节点名后追加自增序号(如 lock-0000000001),保证节点有序,可用于判断“谁是第一个获取锁的客户端”。
2. 实现步骤:3 步完成“加锁-执行业务-解锁”
以“排他锁”(最常用,同一时间仅一个客户端持有)为例,假设提前在 ZooKeeper 中创建一个固定的“锁根节点”(如 /distribute-lock,持久节点),客户端操作流程如下:
步骤 1:客户端加锁(尝试获取锁)
1.客户端在锁根节点下,创建一个 临时顺序节点(如 /distribute-lock/lock-),ZooKeeper 会自动生成带序号的节点名(如 /distribute-lock/lock-0000000001)。
2.客户端获取锁根节点下的所有子节点,并按序号从小到大排序。
3.检查自己创建的节点是否是“序号最小的子节点”:
若是:加锁成功,开始执行业务逻辑。
若不是:找到自己节点的“前一个序号更小的节点”(前驱节点),并对该前驱节点注册一个 “节点删除监听”(WatchedEvent)——当前驱节点被删除时,ZooKeeper 会通知当前客户端。
步骤 2:客户端等待锁(阻塞逻辑)
加锁失败的客户端会进入“阻塞状态”,等待前驱节点的删除通知:
若收到前驱节点被删除的通知(说明前驱客户端释放了锁),则重新执行“步骤 1 的第 2、3 步”,再次判断自己是否是最小节点,直到加锁成功。
此过程避免“轮询”(减少资源消耗),通过监听机制实现“精准唤醒”。
步骤 3:客户端解锁(释放锁)
客户端执行业务逻辑完成后,主动删除自己在锁根节点下创建的临时顺序节点:
节点删除后,ZooKeeper 会自动通知该节点的“后继节点”(下一个等待锁的客户端),触发其重新检查是否能加锁。
若客户端在执行业务时宕机(与 ZooKeeper 断开连接),其创建的临时节点会被 ZooKeeper 自动删除,同样会触发后继节点的唤醒,避免死锁。
3. 关键优化:避免“惊群效应”
早期简单实现中,若所有等待客户端都监听“最小节点”,会导致最小节点删除时所有客户端被唤醒(惊群),浪费资源。 而上述“监听前驱节点”的设计,能确保只有一个客户端被唤醒(当前驱节点删除时,仅通知其后继节点),彻底解决惊群问题,提升效率。
总结
ZooKeeper 分布式锁的核心是“临时顺序节点 + 前驱节点监听”:
临时节点保证宕机自动释放锁,避免死锁;
顺序节点保证按创建顺序排队,公平获取锁;
前驱监听避免惊群,提升性能。 最终实现“同一时间仅一个客户端持有锁”的分布式协调效果。