SpringBoot整合Zookeeper做分布式锁

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: SpringBoot整合Zookeeper做分布式锁

环境准备

zookeeper准备

首先你需要一个zookeeper服务器,或者是一个zookeeper集群。我已经准备好了一个zookeeper集群,如图:

当然一个单节点的zookeeper也可以搭建分布式锁。如果你还没有zookeeper,那么你可以参考我写的搭建zookeeper集群的文章:https://blog.csdn.net/m0_51510236/article/details/132834141

SpringBoot项目准备

这个步骤比较简单,我的代码已经提交至公共代码仓库,代码仓库地址为:https://gitcode.net/m0_51510236/zookeeper-distribution-lock

代码编写

pom.xml依赖文件

首先我们需要添加一下SpringCloudZookeeper的依赖,因为是只使用到了SpringCloud的zookeeper模块,所以没必要引入整个SpringCloud,如果你已经引入了整个SpringCloud,那么就没必要引入该模块了,如图:

其次我们要在dependency里面引入zookeeper的依赖:

<!-- Zookeeper 客户端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper</artifactId>
</dependency>
<!-- Zookeeper 分布式锁 -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
</dependency>

然后我们还需要引入SpringBoot测试模块的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

整体pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.15</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>city.yueyang</groupId>
    <artifactId>zookeeper-distribution-lock</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zookeeper-distribution-lock</name>
    <description>zookeeper分布式锁</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- Zookeeper 客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper</artifactId>
        </dependency>
        <!-- Zookeeper 分布式锁 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-zookeeper-dependencies</artifactId>
                <version>3.1.4</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yaml文件

application.yaml是SpringCloud的配置文件,我们只需要配置zookeeper的地址即可,文件内容为:

spring:
  cloud:
    zookeeper:
      # 可以只写一个,如果是有多个节点的zookeeper集群,那么多个节点用英文逗号隔开
      connect-string: 192.168.1.181:2181,192.168.1.182:2181,192.168.1.183:2181

Runnable类

我们需要建立一个线程池来模拟多线程整合zookeeper做分布式锁,那么我们需要一个Runnable类来定义多线程的任务,java代码为:

package city.yueyang.lock.thread;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
/**
 * <p></p>
 *
 * @author XiaoHH
 * @version 1.0.0
 * @date 2023-09-13 星期三 20:44:59
 * @file DistributionLockRunnable.java
 */
public class DistributionLockRunnable implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(DistributionLockRunnable.class);
    /**
     * 分布式锁对象
     */
    private final InterProcessMutex lock;
    /**
     * CountDownLatch锁,避免多线程没有执行完就退出程序
     */
    private final CountDownLatch countDownLatch;
    public DistributionLockRunnable(InterProcessMutex lock, CountDownLatch countDownLatch) {
        this.lock = lock;
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        try {
            log.info("线程名:{},尝试获取锁", threadName);
            // 获取锁
            lock.acquire();
            log.info("线程名:{},获取锁成功,正在处理数据", threadName);
            Thread.sleep(1000); // 模拟处理数据睡眠一秒钟
            log.info("线程名:{},数据处理成功", threadName);
        } catch (Throwable e) {
            log.error("线程名:{},发生了错误", threadName, e);
        } finally {
            try {
                // 退出锁
                lock.release();
                log.info("线程名:{},锁已释放", threadName);
                // 锁-1
                countDownLatch.countDown();
            } catch (Exception e) {
                log.error("线程名:{},释放锁的时候发生了错误", threadName, e);
            }
        }
    }
}

注意下面两行代码:

lock.acquire(); // 获取锁
lock.release(); // 释放锁

代码里面日志打印已经很详细,这里就不再过多解释

测试类编写

我们编写一个测试用例来测试这个分布式锁:

package city.yueyang.lock;
import city.yueyang.lock.thread.DistributionLockRunnable;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.*;
@SpringBootTest
public class ZookeeperDistributionLockApplicationTests {
    /**
     * 日志记录对象
     */
    private static final Logger log = LoggerFactory.getLogger(ZookeeperDistributionLockApplicationTests.class);
    /**
     * zookeeper的客户端
     */
    @Autowired
    private CuratorFramework curatorFramework;
    /**
     * zookeeper锁的路径,这也会被认为是zookeeper锁的标识
     */
    private static final String LOCK_PATH = "/lock/test-zookeeper-lock";
    @Test
    public void testDistributionLock() {
        // 处理十个任务
        final int taskAmount = 10;
        // 使用 CountDownLatch 锁避免多线程没有执行完程序就停止
        CountDownLatch countDownLatch = new CountDownLatch(taskAmount);
        // 创建zookeeper的锁对象,如果第二个参数也就是path路径是一样的,那么就会被认为是同一把锁
        InterProcessMutex lock = new InterProcessMutex(this.curatorFramework, LOCK_PATH);
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(taskAmount, taskAmount, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(taskAmount));
        for (int i = 0; i < taskAmount; i++) {
            // 创建一个任务
            Runnable task = new DistributionLockRunnable(lock, countDownLatch);
            // 使用线程池去执行它
            executor.execute(task);
        }
        try {
            // 等待所有的countDown锁执行完毕本线程结束
            countDownLatch.await();
        } catch (InterruptedException e) {
            log.error("countdown锁被终止了");
        } finally {
            // 终止线程池
            executor.shutdown();
        }
    }
}

创建锁对象的代码主要是下面这行:

InterProcessMutex lock = new InterProcessMutex(this.curatorFramework, "锁路径的字符串");

重点是第二个参数,是锁路径。无论new出多少个锁对象,只要锁路径的字符串是一样的,就会被zookeeper认为是同一把锁

测试代码

我们直接运行这个测试用例,可以发现都是有序的获取锁,并没有并发执行的问题,这也证明了分布式锁搭建成功:

代码已提交到公共代码仓库。代码仓库地址:https://gitcode.net/m0_51510236/zookeeper-distribution-lock

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
1月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
2月前
|
监控 NoSQL Java
分布式锁实现原理问题之ZooKeeper的观察器(Watcher)特点问题如何解决
分布式锁实现原理问题之ZooKeeper的观察器(Watcher)特点问题如何解决
|
9天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
46 1
|
1月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
1月前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
2月前
|
NoSQL 前端开发 算法
Redis问题之Redis分布式锁与Zookeeper分布式锁有何不同
Redis问题之Redis分布式锁与Zookeeper分布式锁有何不同
|
负载均衡 Java Spring
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式(2)
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
115 0
|
Dubbo Java 应用服务中间件
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式(1)
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
124 0
|
负载均衡 Dubbo Java
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
SpringBoot学习笔记-14:第十四章-SpringBoot 与分布式
118 0
|
1天前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的音乐推荐管理系统
基于Java+Springboot+Vue开发的音乐推荐管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的音乐推荐管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
31 8
基于Java+Springboot+Vue开发的音乐推荐管理系统