1. 分布式锁概述

在分布式系统中,为了保证共享资源在并发访问下的数据一致性,需要引入分布式锁。分布式锁是一种在分布式环境下控制多个进程对共享资源进行互斥访问的机制。它与单机环境下的锁(如Java中的synchronizedLock)不同,单机锁只能解决同一JVM内部的并发问题,而分布式锁则需要解决跨JVM、跨机器的并发问题。

2. ZooKeeper实现分布式锁的原理

ZooKeeper是一个分布式协调服务,它提供了数据一致性、高可用性等特性,非常适合用于实现分布式锁。ZooKeeper实现分布式锁主要利用了其以下特性:

2.1 临时顺序节点(EPHEMERAL_SEQUENTIAL)

ZooKeeper的节点可以设置为临时(Ephemeral)和顺序(Sequential)类型。临时节点会在创建该节点的客户端会话结束时自动删除。顺序节点则会在创建时自动在节点名称后面附加一个单调递增的数字。

利用这两个特性,可以实现分布式锁的“排队”机制:

  1. 创建锁节点:客户端在ZooKeeper上创建一个持久化的父节点,例如/locks,作为所有锁的根目录。
  2. 竞争锁:当一个客户端想要获取锁时,它会在/locks父节点下创建一个临时顺序子节点,例如/locks/lock-0000000001
  3. 判断是否获得锁:客户端获取/locks下所有子节点的列表,并判断自己创建的子节点是否是其中序号最小的。如果是,则表示成功获取锁。
  4. 监听前一个节点:如果客户端创建的子节点不是序号最小的,说明前面还有其他客户端持有锁。此时,该客户端会监听(Watch)比自己序号小的前一个节点。例如,如果客户端创建的是/locks/lock-0000000003,它会监听/locks/lock-0000000002
  5. 释放锁:当持有锁的客户端完成操作后,会删除自己创建的临时节点。由于是临时节点,即使客户端崩溃,该节点也会被ZooKeeper自动删除,从而释放锁。
  6. 唤醒等待者:当被监听的前一个节点被删除时,ZooKeeper会通知监听它的客户端。收到通知的客户端会再次检查自己是否是当前序号最小的节点,如果是,则获取锁。

2.2 节点监听机制(Watcher)

ZooKeeper的Watcher机制允许客户端在节点状态发生变化时(如节点创建、删除、数据改变等)接收到通知。这在分布式锁的实现中至关重要,它避免了客户端频繁地去查询节点状态,从而减少了不必要的网络开销和“羊群效应”(Herd Effect)。

“羊群效应”是指当一个节点发生变化时,所有等待的客户端都被唤醒,然后它们又同时去竞争锁,导致不必要的资源消耗。通过让每个客户端只监听它前面一个节点,可以有效地避免这种问题,实现“首尾相接”的通知机制,保证了锁的传递有序且高效。

2.3 临时节点的自动删除

ZooKeeper的临时节点特性保证了即使客户端在持有锁期间崩溃,其创建的临时节点也会被ZooKeeper自动删除,从而避免了死锁的发生。这大大提高了分布式锁的健壮性。

3. ZooKeeper分布式锁的实现步骤

基于上述原理,实现ZooKeeper分布式锁的典型步骤如下:

  1. 连接ZooKeeper:客户端首先需要建立与ZooKeeper集群的连接。
  2. 创建父节点:在ZooKeeper中创建一个持久化的根节点,例如/distributed_locks,用于存放所有分布式锁的子节点。
  3. 获取锁
    a. 客户端在/distributed_locks下创建一个临时顺序节点,例如/distributed_locks/lock_
    b. 获取/distributed_locks下所有子节点的列表。
    c. 判断自己创建的节点是否是所有子节点中序号最小的。如果是,则获取锁成功。
    d. 如果不是,则找到比自己序号小的前一个节点,并对其设置Watcher监听。
    e. 进入等待状态,直到接收到前一个节点删除的通知。
    f. 收到通知后,重复步骤b,再次判断是否获取锁。
  4. 释放锁
    a. 执行完业务逻辑后,删除自己创建的临时顺序节点。
    b. 关闭ZooKeeper连接。

4. Java代码示例 (基于Curator框架)

在Java中,通常使用Apache Curator框架来操作ZooKeeper,因为它封装了许多ZooKeeper的复杂操作,提供了更高级别的API,包括分布式锁的实现。Curator提供了InterProcessMutex来实现可重入的分布式排他锁。

首先,添加Maven依赖:

<dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</n> <!-- 包含分布式锁的实现 --><version>5.2.0</version>
</dependency>
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>5.2.0</version>
</dependency>
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-client</artifactId><version>5.2.0</version>
</dependency>

然后是代码示例:

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;import java.util.concurrent.TimeUnit;public class ZkDistributedLockExample {private static final String ZK_ADDRESS = "127.0.0.1:2181"; // ZooKeeper地址private static final String LOCK_PATH = "/distributed_lock"; // 锁的路径public static void main(String[] args) {CuratorFramework client = null;try {// 1. 创建Curator客户端client = CuratorFrameworkFactory.builder().connectString(ZK_ADDRESS).sessionTimeoutMs(60000).connectionTimeoutMs(30000).retryPolicy(new ExponentialBackoffRetry(1000, 3)) // 重试策略:初始等待1秒,最多重试3次.build();// 2. 启动客户端client.start();client.blockUntilConnected(); // 阻塞直到连接成功System.out.println(Thread.currentThread().getName() + " ZooKeeper客户端连接成功!");// 3. 创建分布式锁实例InterProcessMutex lock = new InterProcessMutex(client, LOCK_PATH);// 模拟多个线程竞争锁for (int i = 0; i < 5; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " 尝试获取锁...");if (lock.acquire(10, TimeUnit.SECONDS)) { // 尝试获取锁,最多等待10秒try {System.out.println(Thread.currentThread().getName() + " 成功获取锁!执行业务逻辑...");// 模拟业务逻辑处理时间Thread.sleep(2000);} finally {lock.release(); // 释放锁System.out.println(Thread.currentThread().getName() + " 释放锁。");}} else {System.out.println(Thread.currentThread().getName() + " 获取锁失败!");}} catch (Exception e) {e.printStackTrace();}}, "Thread-" + i).start();}// 等待所有线程执行完毕Thread.sleep(15000);} catch (Exception e) {e.printStackTrace();} finally {if (client != null) {client.close();}}}
}

代码说明:

  • CuratorFrameworkFactory.builder().build():用于创建Curator客户端实例,连接ZooKeeper集群。
  • ExponentialBackoffRetry:重试策略,当连接ZooKeeper失败时,会按照指数退避的方式进行重试。
  • InterProcessMutex(client, LOCK_PATH):创建InterProcessMutex实例,它代表了一个可重入的分布式排他锁。LOCK_PATH是锁在ZooKeeper上的路径。
  • lock.acquire(10, TimeUnit.SECONDS):尝试获取锁,如果10秒内未能获取到锁,则返回false。这是一个阻塞方法,直到获取到锁或超时。
  • lock.release():释放锁。务必在finally块中调用,确保锁总是被释放

5. ZooKeeper分布式锁的优缺点

5.1 优点

  • 高可用性:ZooKeeper集群本身具有高可用性,只要集群中大多数节点正常工作,分布式锁服务就能正常提供。
  • 可靠性:利用临时顺序节点和Watcher机制,能够有效避免死锁,并且在客户端崩溃时自动释放锁。
  • 公平性:通过顺序节点,可以实现公平锁,保证先到先得。
  • 避免羊群效应:通过只监听前一个节点,避免了所有等待客户端同时被唤醒的问题。

5.2 缺点

  • 性能相对较低:与基于Redis等内存数据库实现的分布式锁相比,ZooKeeper的性能相对较低,因为每次加锁和释放锁都需要与ZooKeeper集群进行网络通信,涉及到节点的创建、删除和监听,这些操作都需要经过ZooKeeper的Leader节点处理并同步到Follower节点,有一定的延迟。
  • 实现复杂度较高:虽然Curator框架简化了开发,但其底层原理和机制相对复杂,需要对ZooKeeper有深入的理解才能更好地使用和排查问题。
  • 依赖ZooKeeper集群:系统的可用性依赖于ZooKeeper集群的稳定性。

6. 最佳实践

  • 选择合适的锁路径:为不同的业务场景或共享资源定义清晰、有意义的锁路径。
  • 合理设置会话超时时间:ZooKeeper的会话超时时间决定了客户端与服务器断开连接后,临时节点被删除的时间。应根据业务需求和网络状况合理设置,避免过短导致误释放锁,或过长导致死锁。
  • 使用Curator框架:强烈推荐使用Apache Curator等成熟的ZooKeeper客户端框架,它们提供了丰富的特性和更稳定的API,简化了分布式锁的实现。
  • finally块中释放锁:确保无论业务逻辑是否发生异常,锁都能被正确释放,防止死锁。
  • 考虑锁的粒度:根据业务需求,选择合适的锁粒度。过粗的粒度会降低并发性,过细的粒度会增加锁的开销。
  • 监控ZooKeeper集群:对ZooKeeper集群进行实时监控,包括连接状态、节点数量、延迟等指标,确保其健康运行。

7. 总结

ZooKeeper作为一款优秀的分布式协调服务,为分布式锁的实现提供了可靠的基础。通过其临时顺序节点和Watcher机制,可以构建出高可用、可靠且公平的分布式锁。虽然其性能可能不如基于内存数据库的方案,但在对锁的可靠性和一致性要求较高的场景下,ZooKeeper分布式锁是一个非常好的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87929.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87929.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87929.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux线程——基础全解

一、什么是线程&#xff08;Thread&#xff09;&#xff1f;✅ 定义&#xff1a;线程是程序执行的最小单位。即线程&#xff08;Thread&#xff09;是操作系统能够进行运算调度的最小单位&#xff0c;它被包含在进程之中&#xff0c;是进程中的实际运作单位。一个进程可以并发多…

Java基础--封装+static

目录 什么是封装&#xff1f; 什么是访问限定符&#xff1f; static静态修饰符 用static修饰的类变量或类方法的注意事项&#xff1a; 什么是封装&#xff1f; 封装是面向对象的三大特性之一&#xff0c;指的是将一个类中的实现细节进行隐藏&#xff0c;对外只提供一些开放…

DAY 51 复习日

作业&#xff1a;day43的时候我们安排大家对自己找的数据集用简单cnn训练&#xff0c;现在可以尝试下借助这几天的知识来实现精度的进一步提高import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as trans…

针对网络爬虫的相关法律法规整理

在中国&#xff0c;网络爬虫的法律法规涉及多个层面&#xff0c;包括个人信息保护、数据安全、网络安全、知识产权、反不正当竞争等。以下是详细的法律法规分析及合规指南&#xff1a; 1. 核心法律法规及适用场景​ ​​&#xff08;1&#xff09;《民法典》——隐私权与个人信…

1.1_5_2 计算机网络的性能指标(下)

继续来看计算机网络的性能指标&#xff0c;接下来我们探讨时延&#xff0c;时延带宽积和往返时延&#xff0c;以及信道利用率这几个性能指标。 首先来看时延这个性能指标&#xff0c;英文叫delay&#xff0c;也有的教材&#xff0c;把它翻译为延迟。所谓的时延&#xff0c;就是…

PP-OCRv2:超轻OCR系统的万能包

PP-OCRv2&#xff1a;超轻OCR系统的万能包摘要光学字符识别&#xff08;OCR&#xff09;系统已广泛应用于多种场景&#xff0c;但设计兼顾精度与效率的OCR系统仍具挑战性。我们此前提出的超轻量OCR系统PP-OCR在平衡两者方面取得进展。本文进一步提出PP-OCRv2&#xff0c;通过五…

常见的软件版本开源协议

开源软件许可证核心指南 一、许可证基础分类 1. 宽松型许可证&#xff08;Permissive&#xff09; 核心特征&#xff1a;允许闭源衍生&#xff0c;仅保留版权声明适用场景&#xff1a;商业集成、快速开发代表协议&#xff1a; &#x1f4dc; MIT &#x1f4dc; Apache 2.0 &…

基于FPGA的一维序列三次样条插值算法verilog实现,包含testbench

目录 1.前言 2.算法运行效果图预览 3.算法运行软件版本 4.部分核心程序 5.算法仿真参数 6.算法理论概述 7.参考文献 8.算法完整程序工程 1.前言 三次样条插值是一种在数据拟合和信号处理中广泛应用的技术&#xff0c;它通过构造分段三次多项式来逼近给定的离散数据点&a…

RAG 之 Prompt 动态选择的三种方式

“如果我有5个prompt模板&#xff0c;我想只选择一个每次都自动五选一能做到吗怎么做&#xff1f;” 完全可以做到。这在复杂的RAG或Agentic工作流中是一个非常普遍且关键的需求&#xff0c;通常被称为“条件路由&#xff08;Conditional Routing&#xff09;”或“动态调度&am…

【ROS2 自动驾驶学习】02-安装ROS2及其配套工具

目录 一、设置语言环境 二、添加存储库 三、添加软件源 四、安装ROS2 五、配置环境 六、测试ROS2 七、安装一些工具 7.1 terminator 7.2 colcon工具 7.3 tf工具 7.4 joint-state-publisher工具 7.5 urdf 八、安装三方库 8.1 Eigen 8.2 yaml-cpp 8.3 matplotl…

系统学习Python——并发模型和异步编程:基础知识

分类目录&#xff1a;《系统学习Python》总目录 并行是并发的一种特殊情况。**所有并行系统都是并发的&#xff0c;但不是所有并发系统都是并行的。**在21世纪初&#xff0c;我们可以使用单核设备在GNU Linux上同时处理100个进程。一台拥有4个CPU核的现代笔记本计算机&#xff…

睿尔曼系列机器人——以创新驱动未来,重塑智能协作新生态(下)

在智能制造与人工智能深度融合的当下&#xff0c;机器人技术正经历从 “功能替代” 到 “价值共创” 的深刻跃迁。睿尔曼&#xff0c;作为全球超轻量仿人机械臂领域的先行者&#xff0c;始终秉持 “让机器人触手可及” 的使命&#xff0c;凭借底层技术的突破性进展&#xff0c;…

表征工程(Representation Engineering, RepE)

表征工程(Representation Engineering, RepE) 近年来,表征工程(Representation Engineering, RepE)在提升AI系统透明度和可控性方面取得了显著进展。 一、大模型可解释性与可控性的突破 核心论文:《Representation Engineering: A Top-Down Approach to AI Transparen…

国产ARM+FPGA工业开发平台——GM-3568JHF

一、引言 随着物联网和国产替代需求的快速发展&#xff0c;嵌入式系统面临计算性能与硬件灵活性的双重挑战。GM-3568JHF开发板基于国产“ARMFPGA”异构架构&#xff0c;结合瑞芯微RK3568J处理器与紫光同创Logos-2 FPGA芯片&#xff0c;支持国产自主操作系统&#xff0c;满足通…

RISCV Linux 虚拟内存精讲系列一 Sv39

笔者认为&#xff0c;Linux 操作系统&#xff08;Operating System&#xff09;最核心的机制是虚拟内存&#xff08;Virtual Memory&#xff09;。因为&#xff0c;操作系统主要作用是将硬件环境抽象起来&#xff0c;给在其中运行的应用&#xff08;Applications&#xff09;提…

【apply from: “$flutterRoot/packages/flutter_tools/gradle/flutter.gradle“作用】

这行代码的作用是将 Flutter 的 Gradle 构建脚本集成到 Android 项目中&#xff0c;具体细节如下&#xff1a;作用解析&#xff1a;引入 Flutter 构建逻辑 flutter.gradle 是 Flutter SDK 的核心构建脚本&#xff0c;它负责&#xff1a; 编译 Dart 代码为原生二进制文件&#x…

深入理解JavaScript设计模式之命令模式

深入理解JavaScript设计模式之命令模式 文章目录深入理解JavaScript设计模式之命令模式定义简单命令模式组合命令模式使用命令模式实现文本编辑器目标关键类说明实现的效果交互逻辑流程所有代码&#xff1a;总结定义 命令模式也是设计模式种相对于变焦简单容易理解的一种设计模…

CSS 网页布局:从基础到进阶

CSS 网页布局&#xff1a;从基础到进阶 引言 随着互联网的飞速发展&#xff0c;网页设计已经成为了一个不可或缺的领域。CSS&#xff08;层叠样式表&#xff09;作为网页设计中的关键工具&#xff0c;用于控制网页元素的样式和布局。本文将为您全面解析CSS网页布局&#xff0c;…

【人工智能】大语言模型(LLM) NLP

大语言模型&#xff08;LLM&#xff09;& NLP1.大语言模型&#xff08;LLM&#xff09;1.1 一句话解释1.2 更形象的比喻1.3 为什么叫 “大” 模型1.4 它能做什么1.5 现实中的例子2.对比 NLP2.1 用 “汽车进化” 比喻 NLP → LLM2.2 为什么说 LLM 属于 NLP2.3 LLM 的 “革命…

Unity HDRP + Azure IoT 的 Python 后端实现与集成方案

Unity HDRP Azure IoT 的 Python 后端实现与集成方案 虽然Unity HDRP本身使用C#开发&#xff0c;但我们可以构建Python后端服务支持物联网系统&#xff0c;并与Unity引擎深度集成。以下是完整的实现方案&#xff1a; 系统架构 #mermaid-svg-qCDb0g9Ik287Cg8X {font-family:&qu…