当前位置: 首页 > news >正文

Zookeeper分布式锁原理

核心基础

在理解原理前,先回顾三个 ZooKeeper 的核心特性:

  1. 临时节点(Ephemeral Nodes):创建该节点的客户端会话(Session)一旦失效(如断开连接),那么这个节点会被自动删除。这保证了即使客户端崩溃,锁也能被自动释放,避免了死锁。
  2. 顺序节点(Sequential Nodes):创建节点时,ZooKeeper 会自动在节点名称后附加一个单调递增的序列号(如 lock-0000000001)。这保证了所有节点的创建顺序是全局唯一的。
  3. Watcher 机制:客户端可以在某个节点上设置监听(Watcher)。当该节点被删除、修改等事件发生时,ZooKeeper 会通知所有设置了 Watcher 的客户端。这是实现客户端间高效协调和通知的关键。

分布式锁的实现流程(以排他锁为例)

假设我们有一个锁的根节点 /locks/my_lock

1. 获取锁(Acquire Lock)
  • 步骤一:创建临时顺序节点
    所有想要获取锁的客户端,都会在 /locks/my_lock 下创建一个临时顺序节点,例如:

    • Client A 创建了 /locks/my_lock/lock-0000000001
    • Client B 创建了 /locks/my_lock/lock-0000000002
    • Client C 创建了 /locks/my_lock/lock-0000000003
  • 步骤二:检查自己是否是最小节点
    每个客户端都会获取 /locks/my_lock 下的所有子节点,并按序列号排序。

    • 对于 Client A:它发现自己的节点 lock-0000000001 是序列号最小的节点。那么它就成功获得了锁,可以执行自己的业务逻辑。
    • 对于 Client B:它发现自己的节点 lock-0000000002 不是最小的,前面还有 lock-0000000001。这意味着锁正被 Client A 持有。
    • 对于 Client C:同样,它发现自己不是最小的,锁不可用。
  • 步骤三:监听前一个节点(等待锁)
    Client B 和 Client C 不会不停地轮询询问“轮到我了吗?”。而是采用更高效的方式:

    • Client B:它会向它前面的那个节点(即 lock-0000000001)设置一个 Watcher 监听
    • Client C:它会向它前面的那个节点(即 lock-0000000002)设置一个 Watcher 监听

    这样,每个客户端都只监听它前面的节点,形成一个等待队列。

整个过程如下图所示,它清晰地展示了客户端如何通过创建节点、排序和监听来形成一个有序的等待队列:

请添加图片描述

2. 释放锁(Release Lock)
  • 情况一:正常释放
    Client A 完成业务逻辑后,主动删除它创建的那个临时节点 lock-0000000001

  • 情况二:异常释放
    如果 Client A 在持有锁期间进程崩溃或网络断开,由于它创建的是临时节点,ZooKeeper 会自动检测到会话失效,并自动删除 lock-0000000001

3. 唤醒下一个等待者(Notify Next)
  • lock-0000000001 被删除(无论是主动还是被动)时,ZooKeeper 会通知所有监听了这个节点的客户端。也就是 Client B 会收到一个通知
  • Client B 被唤醒后,它会重复“步骤二”:再次获取 /locks/my_lock 下的所有子节点。
    • 此时子节点是 [lock-0000000002, lock-0000000003]
    • Client B 发现自己的节点 lock-0000000002 现在是最小的了!于是它成功获得了锁。
  • 同理,当 Client B 释放锁,删除 lock-0000000002 后,Client C 会被唤醒,进而获得锁。

这种设计的精妙之处

  1. 避免羊群效应(Herd Effect)
    传统的做法是所有客户端都监听同一个锁节点,释放时所有客户端都被唤醒并同时争抢,给 ZooKeeper 和服务端带来巨大压力。而只监听前一个节点的方式,每次锁释放都只精确地唤醒一个客户端(队列中的下一个),压力非常小。

  2. 天然的公平锁(Fair Lock)
    由于节点顺序是全局唯一的,并且严格按照顺序唤醒,所以每个客户端获取锁的顺序就是它们创建节点的顺序,实现了先来后到的公平性。

  3. 自动防死锁(Deadlock-Free)
    得益于临时节点的特性,任何客户端持有的锁都会在会话结束时自动释放,无需担心因为客户端宕机而导致的锁永久死锁问题。

  4. 可重入性(Reentrancy)(需要客户端实现):
    如果同一个客户端线程想再次获取锁,可以在客户端内存中记录一个计数器(count)。检查锁的持有者时,不仅要看节点序列号,还要看节点名称中是否包含自己的客户端ID。如果是自己持有的,就直接增加计数器并返回成功,而无需再创建节点。


总结:ZooKeeper 分布式锁的核心步骤

步骤操作说明
1. 争抢所有客户端在锁目录下创建临时顺序节点宣告自己的排队资格。
2. 判断获取锁目录下所有子节点,判断自己是否是序号最小的节点。是则成功获锁;否则继续。
3. 等待如果不是最小,则监听自己前面那个节点的删除事件。避免羊群效应,高效等待。
4. 释放业务处理完,主动删除自己创建的那个节点。正常释放锁。
5. 唤醒节点被删除,ZooKeeper 通知下一个监听它的客户端下一个客户端被唤醒,回到步骤2。

在实际开发中,我们通常不直接使用 ZooKeeper 的原生 API 来实现锁,而是使用更高级的封装库,例如 Apache Curator。Curator 提供了一个成熟、稳健的分布式锁实现(InterProcessMutex),直接开箱即用,避免了手动处理各种极端情况(例如连接丢失、重试等)。但理解其底层原理对于诊断问题和设计系统至关重要。

http://www.dtcms.com/a/362729.html

相关文章:

  • java-设计模式-5-创建型模式-建造
  • 科普:为什么在开发板上运行 Qt 程序时需要在命令后加 -platform linuxfb
  • 少儿舞蹈小程序从0到1(5):搭建关于我们页面
  • 深入浅出 RabbitMQ - SpringBoot2.X整合RabbitMQ实战
  • 23种设计模式-抽象工厂模式
  • 蓝桥杯算法之基础知识(4)
  • Mysql杂志(七)
  • Deepin25安装mysql8.4.5
  • 在ROS中获取并发布UBS式传感器的温湿度
  • PostgreSQL(1) FETCH用法
  • 企业数字安全守护神:IT运维管理系统全面解析,构建坚不可摧的防护体系
  • 简陋的RPC
  • 从代码到组件:C语言动态库(DLL)封装与使用终极指南
  • NV115NV119美光固态闪存NV129NV112
  • 加速交通云建设,移动云为我国交通强国目标提供有力支撑
  • AES-GCM和(AES-CBC+SHA2-25-HAMC组合,并且发方通过每次内容,更新iv,填序使用递增数字)算法比较
  • 系统科学核心概念辨析及其在人工智能领域的应用研究:一个整合性分析框架
  • 分布式光纤传感选型 3 问:你的场景该选 DTS、DAS 还是 BOTDA?
  • 解锁WebRTC在数字人领域的无限潜能
  • 面试问题:c++的内存管理方式,delete的使用,vector的resize和reverse,容量拓展
  • 大数据量模块设置渲染性能优化
  • 白电三巨头 2025 年战局:美的领跑破局,海尔稳健筑垒,格力承压求变
  • Spring 中 Hikari 与 Druid 的详细介绍、对比及同类组件分析
  • go-mapus最简单的离线瓦片地图协作
  • 【Linux系统】万字解析,进程间的信号
  • 并发编程——13 线程池ThreadPoolExecutor实战及其原理分析
  • md5sum -c用法详解
  • 【Vue2 ✨】Vue2 入门之旅(八):过渡与动画
  • 基础文本处理工具与文本三剑客其二sed awk
  • unity 中的 gradle building 加速(可能无用,导致包体异常)