Zookeeper 实现分布式锁 -- 基于Curator

news2024/11/28 4:35:28

Zookeeper的四种节点类型

1、持久化节点 :所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点——不会因为创建该节点的客户端会话失效而消失。

2、持久化顺序节点:这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。基于持久顺序节点原理的经典应用-分布式唯一ID生成器。

3、临时节点:和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点,集群zk环境下,同一个路径的临时节点只能成功创建一个,利用这个特性可以用来实现master-slave选举。

4、临时顺序节点:相对于临时节点而言,临时顺序节点比临时节点多了个有序,也就是说,没创建一个节点都会加上节点对应的序号,先创建成功,序号越小。其经典应用为实现分布式锁。
 

原理

Curator内部是通过InterProcessMutex(可重入锁)来在zookeeper中创建临时有序节点实现的,如果通过临时节点及watch机制实现锁的话,这种方式存在一个比较大的问题:所有取锁失败的进程都在等待、监听创建的节点释放,很容易发生"惊群效应",zookeeper的压力是比较大的,而临时有序节点就很好的避免了这个问题,Curator内部就是创建的临时有序节点。

原理

创建临时有序节点,每个线程均能创建节点成功,但是其序号不同,只有序号最小的可以拥有锁,其它线程只需要监听比自己序号小的节点状态即可

基本思路如下:

1、在你指定的节点下创建一个锁目录lock;

2、线程X进来获取锁在lock目录下,并创建临时有序节点;

3、线程X获取lock目录下所有子节点,并获取比自己小的兄弟节点,如果不存在比自己小的节点,说明当前线程序号最小,顺利获取锁;

4、此时线程Y进来创建临时节点并获取兄弟节点 ,判断自己是否为最小序号节点,发现不是,于是设置监听(watch)比自己小的节点(这里是为了发生上面说的惊群效应);

5、线程X执行完逻辑,删除自己的节点,线程Y监听到节点有变化,进一步判断自己是已经是最小节点,顺利获取锁。

 代码

<dependency>
   <groupId>org.apache.curator</groupId>
   <artifactId>curator-recipes</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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.util.concurrent.TimeUnit;
 
/**
 * classname:DistributedLock
 * desc:基于zookeeper的开源客户端Cruator实现分布式锁
 */
public class DistributedLock {
    public static Logger log = LoggerFactory.getLogger(DistributedLock.class);
    private InterProcessMutex interProcessMutex;  //可重入排它锁
    private String lockName;  //竞争资源标志
    private String root = "/distributed/lock/";//根节点
    private static CuratorFramework curatorFramework;
    private static String ZK_URL = "zookeeper1.tq.master.cn:2181,zookeeper3.tq.master.cn:2181,zookeeper2.tq.master.cn:2181,zookeeper4.tq.master.cn:2181,zookeeper5.tq.master.cn:2181";
    static{
        //设置重试策略,每隔一秒,最多重试3次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        curatorFramework= CuratorFrameworkFactory.builder()
                .connectString(ZK_URL)
                .retryPolicy(retryPolicy)
                .build();
        curatorFramework.start();
    }
 
    /**
     * 实例化
     * @param lockName
     */
    public DistributedLock(String lockName){
        try {
            this.lockName = lockName;
            interProcessMutex = new InterProcessMutex(curatorFramework, root + lockName);
        }catch (Exception e){
            log.error("initial InterProcessMutex exception="+e);
        }
    }
 
    /**
     * 获取锁
     */
    public void acquireLock(){
        int flag = 0;
        try {
            //重试2次,每次最大等待2s,也就是最大等待4s
            while (!interProcessMutex.acquire(2, TimeUnit.SECONDS)){
                flag++;
                if(flag>1){  //重试两次
                    break;
                }
            }
        } catch (Exception e) {
           log.error("distributed lock acquire exception="+e);
        }
         if(flag>1){
              log.info("Thread:"+Thread.currentThread().getId()+" acquire distributed lock  fail");
         }else{
             log.info("Thread:"+Thread.currentThread().getId()+" acquire distributed lock  success");
         }
    }
 
    /**
     * 释放锁
     */
    public void releaseLock(){
        try {
        if(interProcessMutex != null && interProcessMutex.isAcquiredInThisProcess()){
            interProcessMutex.release();
            curatorFramework.delete().inBackground().forPath(root+lockName);
            log.info("Thread:"+Thread.currentThread().getId()+" release distributed lock  success");
        }
        }catch (Exception e){
            log.info("Thread:"+Thread.currentThread().getId()+" release distributed lock  exception="+e);
        }
    }
}

技巧 -- 添加idea,zookeeper插件

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postgres源码解析38 表创建执行全流程梳理--3

本文结合实例讲解表创建执行流程 [CREATE TABLE wp_shy(id int primary key, name carchar(20))],相关知识回顾见&#xff1a; postgres源码解析38 表创建执行全流程梳理–1 postgres源码解析38 表创建执行全流程梳理–2 执行流程图 transformCreateStmt函数是表创建真正的入口…

第十四届蓝桥杯模拟赛(第二场)题解·2022年·C/C++

前言 本场比赛是校内赛&#xff0c;总体感觉是DP规划比赛和简单数据结构比赛&#xff0c;但是要细心一点就可以了。 因为不知道答案&#xff0c;所以本题解只有一点参考意义&#xff0c;欢迎评论区和小熊同学讨论。 **不保证答案一定就是对的&#xff01;&#xff01;&#x…

退役了,总结的ACM近年区域赛的所有题型

之前写了个退役文章记录想记录下&#xff0c;但是没有内容&#xff0c;还是给删了&#xff0c;所以前面的是退役小记&#xff0c;后面是我个人写近2年所有区域赛场次记录的题型 目录 退役小记&#xff08;没兴趣可以不看&#xff09; 这里简单记下我的acm生涯 省赛和三场区…

Miniconda:在pycharm的terminal中无法使用Conda命令

在pycharm的terminal中无法使用Conda命令 问题&#xff1a; 在本地下载好conda系列后&#xff0c;在pycharm的terminal中无法使用conda命令 问题分析&#xff1a; 说的很清楚了&#xff0c;是因为当前支持的shells没有初始化conda 所以我们只需要把Shell 路径改成激活cond…

腾讯云架构师整理总结的MySQL性能优化和高可用架构实践文档

前言 有人调侃我们说&#xff1a; 程序员不如送外卖。送外卖是搬运食物&#xff0c;自己是搬运代码&#xff0c;都不产出新的东西……透支体力&#xff0c;又消耗健康&#xff0c;可替代性极强&#xff0c;30岁之后就要面临被优化的危险……想跳槽&#xff0c;但是更高的平台…

wordpress改成https网址方法

我们建议搭建wordpress使用宝塔更为方便&#xff0c;另外也可以选择WDCP或者phpstudy也不错。本次教程适用于宝塔。 老样子买一台云服务器&#xff0c;建议找一些主流云服务器提供商&#xff0c;然后买一台Linux系统的然后安装好宝塔控制面板&#xff0c;创建一个站点&#xf…

【T+】畅捷通T+服务管理中,异步任务服务(TPlusPopAsyncTaskService1700)无法启动

【问题描述】 畅捷通T产品&#xff0c; 服务管理中的【异步任务服务&#xff08;TPlusPopAsyncTaskService1700&#xff09;】一直处于停止状态&#xff0c;且点击启动没有任何反应。 【解决方法】 【排查过程】 首先&#xff1a;检查数据库配置&#xff0c;以及网站端口配置…

可以写进简历的软件测试电商项目,不进来get一下?

前言 说实话&#xff0c;在找项目的过程中&#xff0c;我下载过&#xff08;甚至付费下载过&#xff09;N多个项目、联系过很多项目的作者&#xff0c;但是绝大部分项目&#xff0c;在我看来&#xff0c;并不适合你拿来练习&#xff0c;它们或多或少都存在着“问题”&#xff…

Linux C中对json格式数组数据的生成与解析

在网络通信中&#xff0c;数据经常被做成json格式的来进行传输。那么我们怎么在linux系统中去做json格式的数据呢&#xff1f;怎么将接收到的json格式的数据解析出来呢&#xff1f; 1、linux json库的安装 &#xff08;1&#xff09;下载json-c源码包 &#xff08;2&#xff09…

Redis 内存淘汰和过期删除策略

提起使用Redis的优点&#xff0c;大家可以列举出许多&#xff0c;比如&#xff1a;数据存储在内存&#xff0c;读写速度快&#xff0c;性能优异。比如数据持久化&#xff0c;便于数据备份及恢复等等。 分布式服务系统平台发展至今&#xff0c;Redis活跃在平台的各个领域&#…

如何写单元测试

单元测试理论知识 什么是单元测试&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。通常而言&#xff0c;一个单元可能是单个程序、类、对象、方法等。 为什么需要单元测试 为什么要做单元测试&#xf…

Linux学习-63-源码包服务管理方法

14.5 源码包服务管理&#xff08;启动与自启动&#xff09; 14.5.1 源码包服务的启动管理 源码包服务中所有的文件都会安装到指定目录当中&#xff0c;并且没有任何垃圾文件产生&#xff08;Linux 的特性&#xff09;&#xff0c;所以服务的管理脚本程序也会安装到指定目录中…

[东华杯2021] ezgadget

复现环境配置&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1t5-fV7SUETDEI5-qbZZQrw 提取码&#xff1a;8do5运行 java -jar ezgadget.jar访问127.0.0.1:8888就可以了 分析&#xff1a; ToStringBean.java package com.ezgame.ctf.tools;import java.io.Seriali…

Nevrona Rave Reports基于报表库

Nevrona Rave Reports基于报表库 Rave Reports被描述为一套复杂的Delphi和CBuilder组件&#xff0c;它能够实现强大的进化过程&#xff0c;并为用户和开发人员提供灵活的数据库覆盖。Rave可视报表设计器基本上是一个基于组件的系统&#xff0c;它是专门为覆盖范围而编写的。与传…

Discourse 为什不建议使用 Gmail 的 SMTP

最开始我们也用了 Gmail 的 SMTP 服务。 这里有个问题是 Gmail 的日发送邮件限制&#xff0c;很多人可能认为 Gmail 是没有日常发送邮件限制的&#xff0c;通常不是这样的&#xff0c;因为如果你是手工回复和发送邮件的话&#xff0c;这个限制还是很难达到的。 如果是计算机或…

docker镜像如何下载到本地

Docker save 命令 | 菜鸟教程 查看镜像 docker images 保存到本地 docker save 999c20aee5da > /home/artipub.tar 999c20aee5da为镜像ID docker save : 将指定镜像保存成 tar 归档文件。 语法 docker save [OPTIONS] IMAGE [IMAGE...] OPTIONS 说明&#xff1a; -o :…

Xlua在unity中使用luaide打断点

本功能可以让你使用同一个编辑器实现对c#和lua打断点 编辑器&#xff1a;vscode 注&#xff1a;这是luaide的付费版才能使用断点的功能&#xff0c;请尊重原作者的辛苦付出&#xff01; 如有需要请访问官方进行操作,官方链接如下&#xff1a; ShowDoc一个非常适合IT团队的在…

UDS知识整理(六):通讯控制——0x28服务

目录 一、0x28服务&#xff08;通讯控制&#xff09;简介 二、0x28服务信息格式 &#xff08;1&#xff09;请求格式 &#xff08;2&#xff09;正响应格式 &#xff08;3&#xff09;负响应格式 三、0x28服务举例 &#xff08;1&#xff09;打开接收与发送通讯功能 一、…

如何设计分布式系统-分布式事务-TCC?

如何设计分布式系统-分布式事务-2PC、3PC&#xff1f;_技术分子的博客-CSDN博客 TCC 事务模型的思想类似 2PC 提交&#xff0c;下面对比 TCC 和基于 2PC 事务 XA 规范对比。 第一阶段 TCC 中锁定资源。 第二阶段 TCC根据锁定资源情况进行确认和取消操作。 区别 2PC/XA 是数…

【STL常用容器】:string 容器

文章目录前言一、string容器的基本概念二、字符串的创建构造三、string 赋值操作四、string 字符串拼接五、string 查找和替换六、string字符串比较七、string 字符的存取八、string的插入和删除九、string 子串例&#xff1a;取出邮箱中的用户名前言 时不可以苟遇&#xff0c;…