基于 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)

大家都在看

  • Java源码详解系列(十二)–Eureka的使用和源码

    eureka 是由 Netflix 团队开发的针对中间层服务的负载均衡器,在微服务项目中被广泛使用。相比 SLB、ALB 等负载均衡器,eureka 的服务注册是无状态的,扩展起来…

    数据库 2023年6月6日
    079
  • 23种设计模式之迭代器模式

    文章目录 概述 迭代器模式的优缺点 迭代器模式的结构和实现 * 模式结构 模式实现 总结 ; 概述 迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟…

    数据库 2023年6月6日
    076
  • Redis 串行生成顺序编码

    场景:针对于分布式并发环境,易出现编码生成重复问题方案特点:串行操作可避免阻塞加锁,处理效率更高 具体解决方案 private final static String ENTERP…

    数据库 2023年6月6日
    093
  • pg 锁表

    select * from pg_catalog.pg_stat_activity where usename =’gis_bd_app’ and wait…

    数据库 2023年6月6日
    0118
  • springmvc静态资源配置

    <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>o…

    数据库 2023年6月16日
    0104
  • Java线程通信

    Java线程通信 螣蛇乘雾,终为土灰。 多个线程协同工作完成某个任务时就会涉及到线程间通信问题。如何使各个线程之间同时执行,顺序执行、交叉执行等。 一、线程同时执行 创建两个线程a…

    数据库 2023年6月14日
    0100
  • mysql数据备份与恢复之mysqldump和source命令

    use database source dbname.sql 1.导出一个数据库的结构 mysqldump -d dbname -uroot -p > dbname.sql …

    数据库 2023年5月24日
    092
  • 浅谈多线程中数据的绑定和赋值

    我们知道,微软的.NET控件做了大量的工作,用起来还是不错的,一般的数据绑定或者赋值比较简单。如下所示 文本赋值: txtTest.Text = “abc”…

    数据库 2023年6月11日
    080
  • Mybatis-Plus初步上手!!

    1.简介 1.1、特性 2.快速开始 3.配置日志 4.CRUD拓展 4.1、插入 4.2、更新 4.3、查询 4.4、删除 5.性能分析插件 6.条件构造器Wrapper 7.代…

    数据库 2023年6月16日
    094
  • SSM配置文件的连接

    使用ssm框架配置数据库连接时的问题 如果MySQL数据库版本是8.0.11, url配置成了MySql5.0以上版本需要的驱动类名(com.mysql. cj.jdbc.Driv…

    数据库 2023年6月14日
    096
  • SpringBoot操作Oracle

    /* Navicat Premium Data Transfer Source Server : 本地Oracle Source Server Type : Oracle Sour…

    数据库 2023年6月14日
    0112
  • 如何干涉MySQL优化器使用hash join

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前言 实验 总结 前言 数据库的…

    数据库 2023年6月11日
    0106
  • 什么是真正的HTAP?(一)背景篇

    To digitally transform the business, AI must be real-time. For AI to be real-time, we need…

    数据库 2023年5月24日
    0109
  • Typora + PicGo + Gitee 解放你对图片的管理

    计算机环境准备 Typora PicGo nodejs Typora官网: https://typora.io/ PicGo官网: https://picgo.github.io/…

    数据库 2023年6月9日
    092
  • MySQL实战45讲 3

    03 | 事务隔离:为什么你改了我还看不见? 事务 Transaction TRX 事务就是 要保证一组数据库操作,要么全部成功,要么全部失败。 MySQL 原生的 MyISAM …

    数据库 2023年6月16日
    0108
  • Python环境安装

    一、下载地址: Python:Download Python | Python.org PyCharm:Download PyCharm: Python IDE for Profe…

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