基于 ZooKeeper 的分布式锁实现

ZK 基本概念

基于 ZooKeeper 的分布式锁实现
  • apache hadoop 下面的子项目,是一个树形目录服务
  • 字面意思就是动物管理员,诞生之初用来管理 hadoop(大象)、Hive(蜜蜂)、Pig(小猪)
  • 用于分布式应用的协调服务
  • 提供的主要常用功能:1 集群管理(注册中心) 2 分布式锁 3 配置管理(配置中心)

; ZK 数据结构

跟 Unix 文件系统非常类似,可以看做是一颗 ,每个节点叫做 znode,每一个节点可以通过路径来标识,znode 节点分为两种类型:

  • 持久节点:该数据节点被创建后,就会一直存在于 ZooKeeper 服务器上,直到有删除操作来主动删除这个节点,详细来说还可以分为 普通持久节点和 带顺序号的持久节点。
  • 临时节点:临时节点的生命周期和客户端会话绑定在一起,客户端会话失效,则这个节点就会被自动清除,同样分为普通临时节点和带顺序号的临时节点。
    基于 ZooKeeper 的分布式锁实现

统一配置管理

我们可以将 common.yml 这份配置放在 ZooKeeper 的 znode 节点中,系统 A、B、C 监听该 znode 节点有无变更,如果变更了,及时响应。

基于 ZooKeeper 的分布式锁实现

; 统一命名服务

系统 A、B、C 通过访问 /myMachine 这个 znode 节点就能拿到对应的 IP 地址

基于 ZooKeeper 的分布式锁实现

分布式锁实现

利用 Zookeeper 可以创建 带顺序号的临时节点的特性来实现分布式锁,系统 A、B、C 都去访问 /locks 节点,访问的时候会创建带顺序号的临时节点,比如,系统 A 创建了 id_000000 节点,系统 B 创建了 id_000002 节点,系统 C 创建了 id_000001 节点。

基于 ZooKeeper 的分布式锁实现
接着,拿到 /locks 节点下的所有子节点(id_000000, id_000001, id_000002),判断自己创建的是不是最小的那个节点:
  • 是,则拿到锁(执行完操作后,删掉创建的节点,即表示释放锁)
  • 否,则监听比自己要小 1 的节点变化

; 举个例子:

1 系统 A 拿到 /locks 节点下的所有子节点,经过比较,发现自己(id_000000),是所有子节点最小的,所以得到锁
2 系统 B 拿到 /locks 节点下的所有子节点,经过比较,发现自己(id_000002),不是所有子节点最小的。所以监听比自己小 1 的节点 id_000001 的状态
3 系统 C 拿到 /locks 节点下的所有子节点,经过比较,发现自己(id_000001),不是所有子节点最小的。所以监听比自己小 1 的节点 id_000000 的状态
……

等到系统 A 执行完操作以后,将自己创建的节点删除(id_000000)。通过监听,系统 C 发现id_000000 节点已经删除,发现自己已经是最小的节点,于是顺利拿到锁。

实践:ZK 客户端框架 Curator

Curator 是 Netflix 公司开源的一套 ZooKeeper 客户端框架,解决了很多 ZooKeeper 客户端非常底层的细节开发工作,包括连接重连、反复注册 Watcher 和NodeExistsException 异常等。

public class InterprocessLock {
    public static void main(String[] args)  {
        CuratorFramework zkClient = getZkClient();
        String lockPath = "/lock";
        InterProcessMutex lock = new InterProcessMutex(zkClient, lockPath);
        // 模拟50个线程抢锁
        for (int i = 0; i < 50; i++) {
            new Thread(new TestThread(i, lock)).start();
        }
    }

    static class TestThread implements Runnable {
        private Integer threadFlag;
        private InterProcessMutex lock;

        public TestThread(Integer threadFlag, InterProcessMutex lock) {
            this.threadFlag = threadFlag;
            this.lock = lock;
        }

        @Override
        public void run() {
            try {
                lock.acquire();
                System.out.println("第"+threadFlag+"线程获取到了锁");
                // 等到1秒后释放锁
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                try {
                    lock.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static CuratorFramework getZkClient() {
        String zkServerAddress = "192.168.3.39:2181";
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
        CuratorFramework zkClient = CuratorFrameworkFactory.builder()
                .connectString(zkServerAddress)
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .build();
        zkClient.start();
        return zkClient;
    }
}

锁的释放

创建的节点为临时会话顺序节点(EPHEMERAL_SEQUENTIAL),即该节点会在客户端链接断开时被删除,也可以手动 release 锁删除该节点。

可重入性

ZooKeeper 的锁是可重入的,即同一个线程可以多次获取锁,只有第一次真正的去创建临时会话顺序节点,后面的获取锁都是对重入次数加 1。相应的,在释放锁的时候,前面都是对锁的重入次数减 1,只有最后一次才是真正的去删除节点。

客户端故障检测

客户端会在会话的有效期内,向服务器端发送 PING 请求进行心跳检查,证明自己还存活。服务器端接收到客户端的请求后,会进行对应的客户端会话激活,会话激活就是延长该会话的存活期。如果有会话一直没有激活,那么说明该客户端出问题了,服务器端的会话超时检测任务就会检查出那些一直没有被激活的与客户端的会话,然后对其进行清理,清理中有一步就是删除临时会话节点(包括临时会话顺序节点)。这就保证了 ZooKeeper 分布锁的容错性,不会因为客户端的意外退出,导致锁一直不释放,其他客户端获取不到锁。

数据一致性

ZooKeeper 服务器集群一般由一个 leader 节点和其他的 follower 节点组成,数据的读写都是在 leader 节点上进行。当一个写请求过来时,leader 节点会发起一个 proposal,
待大多数 follower 节点都返回 ack 之后,才允许客户端 commit,待大多数 follower 节点都对这个 proposal 进行 commit 了,leader 才会对客户端返回请求成功;如果之后leader 挂掉了,那么会采用 leader 选举算法 zab 协议保证存在最新数据的 follower 节点当选为新的 leader。所以,新的 leader 节点上都会有原来 leader 节点上提交的所有数据,如此保证客户端请求数据的一致性。

Original: https://www.cnblogs.com/pandacode/p/16461564.html
Author: 这个杀手冷死了
Title: 基于 ZooKeeper 的分布式锁实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/575878/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • docker-ckeditor图片img标签style属性自适应

    1,修改ckeditor的源码cofig.js文件 // &#x4E0D;&#x7ED9;&#x56FE;&#x7247;img&#x6DF…

    数据库 2023年6月9日
    0117
  • 2022-08-17 DQL—-子查询,日期格式

    子查询、日期格式 DQL查询语言 子查询 根据结果集中的行数,子查询可以分为以下几类: [En] According to the number of rows in the re…

    数据库 2023年5月24日
    098
  • xcopy自动化案例

    xcopy自动化案例 @echo off title xcopy U8BAK by huanu set "NYR=%date:~0,4%%date:~5,2%%date:…

    数据库 2023年6月9日
    098
  • 如何把 MySQL 备份验证性能提升 10 倍

    JuiceFS 非常适合用来做 MySQL 物理备份,具体使用参考我们的官方文档。最近有个客户在测试时反馈,备份验证的数据准备( xtrabackup –prepare)过程非常…

    数据库 2023年5月24日
    075
  • day03-2无异常退出

    多用户即时通讯系统03 4.编码实现02 4.3功能实现-无异常退出系统 4.3.1思路分析 上述代码运行时,在客户端选择退出系统的时候,可以发现程序并没有停止运行,原因是: 退出…

    数据库 2023年6月11日
    092
  • 项目管理构建工具——Maven(基础篇)

    项目管理构建工具——Maven(基础篇) 在前面的内容中我们学习了JDBC并且接触到了jar包概念 在后面我们的实际开发中会接触到很多jar包,jar包的导入需要到互联网上进行就会…

    数据库 2023年6月14日
    086
  • 解决数据库报错Error 1390: Prepared statement contains too many placeholders的问题

    今天,当您开发一个项目时,您试图一次插入大量数据,但出现了以下错误: [En] Today, when you were developing a project, you tri…

    数据库 2023年5月24日
    0118
  • MVCC – Read View的可见性判断理解

    读了 @SnailMann大佬【MySQL笔记】正确的理解MySQL的MVCC及实现原理 收益颇丰,非常感谢! 但对其中如何判断事务是否可见性还是不太理解,于是作了本文,在原博客基…

    数据库 2023年5月24日
    095
  • mysql常用操作汇总

    工作中经常用会遇到这种情况,可以访问mysql所在的服务器,但是服务器端口不对外暴露(通常因为安全原因)。这时,操作数据库只能通过命令行和 mysql client窗口来实现。我对…

    数据库 2023年6月14日
    095
  • IO模型

    Unix IO模型 对于一个套接字上的输入操作,分为两步: 等待数据准备好(从网络中到达,到内核缓冲区) 将数据从内核缓冲区复制到应用进程缓冲区 I/O模型主要为以下五种: 阻塞I…

    数据库 2023年6月11日
    087
  • SQL语句大全–SQL

    前言 本片博客使用mysql数据库进行数据操作,使用Navicat for mysql 这个IDE进行可视化操作。每个SQL语句都是亲身实验验证的,并且经过自己的思考的。能够保证s…

    数据库 2023年5月24日
    066
  • leetcode 437. Path Sum III 路径总和 III(中等)

    一、题目大意 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也…

    数据库 2023年6月16日
    0104
  • volatility3-windows插件

    volatility3和volatility有很大的区别 查看镜像信息,volatility会进行分析 <span class=”ne-text”>python vol…

    数据库 2023年6月11日
    095
  • Windows安装mysql数据库

    一般我安装mysql用以下两个方法: 一.phpstudy环境下的mysql安装 只需将mysql的bin目录配置到系统环境变量即可, 输入默认密码root即可登录 二.本地直接安…

    数据库 2023年6月16日
    078
  • MySQL实战45讲 1,2

    01 | 基础架构:一条SQL查询语句是如何执行的? Server 层 所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。 存储引擎层负责数据的存储和提取。其架构模…

    数据库 2023年6月16日
    076
  • leetcode 114. Flatten Binary Tree to Linked List 二叉树展开为链表(简单)

    一、题目大意 给你二叉树的根结点 root ,请你将它展开为一个单链表: 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始…

    数据库 2023年6月16日
    0110
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球