分布式锁3: zk实现分布式锁3 使用临时顺序节点+watch监听实现阻塞锁

一  zk实现分布式锁

1.1 使用临时顺序节点 的问题

接上一篇文章,每个请求要想正常的执行完成,最终都是要创建节点,如果能够避免争抢必然可以提高性能。这里借助于zk的临时序列化节点,实现分布式锁

1. 主要修改了构造方法和lock方法:

2.并添加了getPreNode获取前置节点的方法。

存在的问题就是羊群效应。 

1.2  使用临时顺序节点+watch监听实现阻塞锁

假如当前有1000个节点在等待锁,如果获得锁的客户端释放锁时,这1000个客户端都会被唤醒,这种情况称为“羊群效应”。

在这种羊群效应中,zookeeper需要通知1000个客户端,这会阻塞其他的操作,最好的情况应该只唤醒新的最小节点对应的客户端。应该怎么做呢?在设置事件监听时,每个客户端应该对刚好在它之前的子节点设置事件监听,例如子节点列表为/locks/lock-0000000000、/locks/lock-0000000001、/locks/lock-0000000002,序号为1的客户端监听序号为0的子节点删除消息,序号为2的监听序号为1的子节点删除消息。

1.3  使用临时顺序节点+watch监听实现阻塞锁的算法逻辑

1.客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/locks/lock-0000000000,第二个为/locks/lock-0000000001,以此类推;

2.客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;

3.执行业务代码;

4.完成业务流程后,删除对应的子节点释放锁。

二  操作步骤

2.1 代码

2.1.1 zk的客户端

初始化则创建临时序列化节点

2.1.2 分布式锁代码

package com.atguigu.distributed.lock.config;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.*;


import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @ClassName: ZkDistributedTempLock
 * @Description: TODO
 * @Author: admin
 * @Date: 2024/01/06 11:05:09 
 * @Version: V1.0
 **/
public class ZkDistributedTempLock implements Lock {
    private static final String ROOT_PATH = "/d-zk";
    private String path;
    private ZooKeeper zooKeeper;

    public ZkDistributedTempLock(ZooKeeper zooKeeper,String lockName) throws KeeperException, InterruptedException {
        this.zooKeeper = zooKeeper;
       // this.path = ROOT_PATH + "/" + lockName+"-";
        this.path = zooKeeper.create(ROOT_PATH + "/" + lockName + "-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
    }

    public void lock() {
        try {
            String preNode = getPreNode(path);
            // 如果该节点没有前一个节点,说明该节点时最小节点,放行执行业务逻辑
            if (StringUtils.isEmpty(preNode)) {
                return;
            } else {//有前一个节点,阻塞,对前一个节点进行监听
                CountDownLatch countDownLatch = new CountDownLatch(1);
                if (this.zooKeeper.exists(ROOT_PATH + "/" + preNode, new Watcher() {

                    @Override
                    public void process(WatchedEvent event) {
                        System.out.println("当前节点=="+path+" 前一个节点:"+ROOT_PATH + "/" + preNode);
                        countDownLatch.countDown();
                    }
                }) == null) {
                    return;
                }
                // 阻塞。。。。
                countDownLatch.await();
                return;
            }
        } catch (InterruptedException | KeeperException e) {
            // 重新检查。是否获取到锁
            try {
                Thread.sleep(20);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            lock();
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock(){
        try {
            this.zooKeeper.delete(path, 0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取指定节点的前节点
     * @param path
     * @return
     */
    private String getPreNode(String path){
       System.out.println("path:"+path);
        try {
            // 获取当前节点的序列化号
            Long curSerial = Long.valueOf(StringUtils.substringAfterLast(path, "-"));
            // 获取根路径下的所有序列化子节点
            List<String> nodes = this.zooKeeper.getChildren(ROOT_PATH, false);
            // 判空
            if (CollectionUtils.isEmpty(nodes)){
                return null;
            }

            // 获取前一个节点
            Long flag = 0L;
            String preNode = null;
            for (String node : nodes) {
                // 获取每个节点的序列化号
                Long serial = Long.valueOf(StringUtils.substringAfterLast(node, "-"));
                if (serial < curSerial && serial > flag){
                    flag = serial;
                    preNode = node;
                }
            }

            return preNode;
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public Condition newCondition() {
        return null;
    }
}

2.1.3 service层

2.1.4 controller层

2.2 nginx代理多服务节点访问

1.服务启动

2.nginx启动

3.jemeter访问

2.3 测试

数据库初始化

测试后

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【鸿蒙4.0】安装DevEcoStudio

1.下载安装包 HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者华为鸿蒙DevEco Studio是面向全场景的一站式集成开发环境,&#xff0c;在鸿蒙官网下载或升级操作系统开发工具DevEco Studio最新版本&#xff0c;SDK配置和下载&#xff0c;2.1支持Mac、Windows操作系统。…

静态网页设计——环保网(HTML+CSS+JavaScript)(dw、sublime Text、webstorm、HBuilder X)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1BC4y1v7ZY/?vd_source5f425e0074a7f92921f53ab87712357b 使用技术&#xff1a;HTMLCSSJS&#xff08;…

鸟类分类、鸟类声音相关深度学习数据集大合集

最近收集了一大波和鸟类相关的图片、声音数据集&#xff0c;包含&#xff1a;鸟类分类、鸟类声音识别、鸟类和无人机分类、鸟类状态、鸟类行为等相关数据集。现在分享给大家&#xff01;&#xff01; 1、英国20大园林鸟类的图像数据集 20英国花园鸟类数据集提供了20个类别的3…

我用 midjourney 创作的那些好看的图片

下面这些是个人的midjourney v5的关键词&#xff0c;各种类型都有 抽象画 One piece of original artwork from 1998 , in the style of confucian ideology, pop art-inspired collages, recycled material murals, meticulous military scenes, close-up intensity, grocer…

Android Canvas图层saveLayer剪切clipRect原图对应Rect区域,Kotlin(1)

Android Canvas图层saveLayer剪切clipRect原图对应Rect区域&#xff0c;Kotlin&#xff08;1&#xff09; 上面一个ImageView&#xff0c;下面一个ImageView&#xff0c;两个ImageView同等大小。当手指在上面的ImageView滑动时候&#xff0c;在下面ImageView里面显示对应区域“…

如何使用UUP从windows更新服务器下载windows10原版镜像

UUP是指Windows 10中的一种更新技术&#xff0c;全称为Unified Update Platform。UUP的目标是提供更快、更高效的更新体验&#xff0c;它通过增量更新的方式来更新操作系统&#xff0c;只下载和安装实际变化的部分&#xff0c;而不是整个更新包。这样可以节省带宽和时间&#x…

案例102:基于微信小程序的旅游社交管理系统设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

HAL——SPI

学习目标 掌握SPI配置方式掌握SPI读写操作 学习内容 需求 SPI配置 打开SPI1,选中全双工模式。观察下方自动生成的引脚&#xff0c;是否和自己开发板引脚对应。 修改引脚&#xff0c;来动右侧芯片引脚视图&#xff0c;找到开发板对应引脚&#xff0c;进行修改。

【Python机器学习】线性模型——线性回归

线性回归&#xff0c;又叫普通最小二乘法&#xff0c;是回归问题最简单也是最经典的线性方法。线性回归寻找参数w和b&#xff0c;使得对训练集的预测值与真实的回归目标值y之间的均方误差最小。 均方误差是预测值与真实值之差的平方和除以样本差。线性回归没有参数&#xff0c…

设计模式设计原则——依赖倒置原则(DIP)

DIP&#xff1a;Dependence Inversion Principle 原始定义&#xff1a;High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。 官…

62.网游逆向分析与插件开发-游戏增加自动化助手接口-游戏公告类的C++还原

内容来源于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏红字公告功能的逆向分析-CSDN博客 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;0888e34878d9e7dd0acd08ef…

Android Studio 最新版本首次下载和安装以及汉化教程【+第二次安装使用教程】

&#x1f31f;博主领域&#xff1a;嵌入式领域&人工智能&软件开发 前言&#xff1a;本教程详解首次安装和下载最新版本的Android Studio &#xff0c;以及汉化教程。另外详解当第二次下载使用时解决遇到的问题。 目录 1.Android Studio 下载 2.Android Studio 首次…

机器人相关知识

机器人学&#xff08;Robotics) 一些基础概念 位姿 位姿位置姿态 位姿的表示 刚体 刚性物体是一组粒子的集合&#xff0c;其中任意两个粒子之间的距离保持固定&#xff0c;不受物体运动或施加在物体上的力的影响。 “完全不可变形”的物体就是刚体。 刚体位置 刚性连杆 …

qt信号和槽

Qt是一个跨平台的C图形用户界面应用框架 91年奇趣科技开发 pro工程文件介绍 .pro就是工程文件(project)&#xff0c;它是qmake自动生成的用于生产makefile的配置文件 QT core gui //Qt包含的模块greaterThan(QT_MAJOR_VERSION, 4): QT widgets //大于4版本包含…

实时语义分割模型PP-LiteSeg论文解读

paper&#xff1a;PP-LiteSeg: A Superior Real-Time Semantic Segmentation Model official implementation&#xff1a;https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.8/paddleseg/models/pp_liteseg.py 本文的创新点 提出了一种灵活的轻量级解码器&#xf…

OCS2 入门教程(四)- 机器人示例

系列文章目录 前言 OCS2 包含多个机器人示例。我们在此简要讨论每个示例的主要特点。 System State Dim. Input Dim. Constrained Caching Double Integrator 2 1 No No Cartpole 4 1 Yes No Ballbot 10 3 No No Quadrotor 12 4 No No Mobile Manipul…

虾皮一键铺货软件:如何使用一键铺货软件在Shopee平台上上架商品

在如今竞争激烈的电商市场中&#xff0c;卖家们都希望能够快速上架商品&#xff0c;以节省时间和精力&#xff0c;并提高销售效率。而在Shopee&#xff08;虾皮&#xff09;平台上&#xff0c;一键铺货软件成为了许多卖家的首选工具。这些软件不仅可以帮助卖家快速将商品批量上…

Python Asyncio网络编程方法全面解析与实战应用!

更多Python学习内容&#xff1a;ipengtao.com Python的asyncio库是一种强大的异步编程工具&#xff0c;它使得编写高效的网络应用程序变得更加容易。在本文中&#xff0c;我们将深入探讨使用asyncio进行网络编程的方法&#xff0c;包括异步IO、协程、事件循环等方面的内容&…

Android学习(一):Android Studio安装与配置

Android学习&#xff08;一&#xff09;&#xff1a;Android Studio安装与配置 一、安装 下载地址 下载zip文件&#xff0c;免安装。 二、下载资源 启动后&#xff0c;出现该弹框&#xff0c;点击Cancel。 点击Next 默认&#xff0c;点击Next。 点击Next。 点击Finish 开始…

Danil Pristupov Fork(强大而易用的Git客户端) for Mac/Windows

在当今软件开发领域&#xff0c;团队协作和版本控制是非常重要的方面。在这个过程中&#xff0c;Git成为了最受欢迎的版本控制工具之一。然而&#xff0c;对于Git的使用&#xff0c;一个好的客户端是至关重要的。 今天&#xff0c;我们要为大家介绍一款强大而易用的Git客户端—…