使用 lock4j-redis-template-spring-boot-starter 实现redis分布式锁

使用 lock4j-redis-template-spring-boot-starter 实现redis分布式锁

    • [1. 引入依赖](#1. 引入依赖)
    • [2. 配置 Redis](#2. 配置 Redis)
    • [3. 使用分布式锁](#3. 使用分布式锁)
    • [4. 自定义锁配置](#4. 自定义锁配置)
    • [5. 处理锁获取失败的情况](#5. 处理锁获取失败的情况)
    • [6. 高级用法](#6. 高级用法)
    • 7.样例

分布式锁是一种用于在分布式系统中实现并发控制的机制。在分布式系统中,由于多个节点同时访问共享资源,会出现脏读、写冲突等并发问题。分布式锁通过在共享资源的访问前获取锁,来限制只有一个节点可以访问共享资源,从而保证在同一时间只有一个节点可以执行共享资源的操作,避免并发问题的发生。

分布式锁需要满足以下特点:

互斥性:同一时刻只能有一个节点可以获取到锁,其他节点需要等待。

可重入性:同一节点在获取到锁后可以再次获取锁,避免死锁的发生。

安全性:分布式锁需要保证在各种异常情况下都能正确释放锁,避免出现死锁、资源泄露等问题。

高可用性:分布式锁需要在多个节点之间实现协调,保证在节点故障时能够正确进行锁的转移和释放。

常见的分布式锁实现方式有基于数据库、基于缓存、基于ZooKeeper等。选择哪种实现方式要根据具体的业务场景和需求来决定。

现在使用redis缓存来实现简单的分布式锁。

1. 引入依赖

首先,在你的 Spring Boot 项目的 pom.xml 文件中添加 lock4j-redis-template-spring-boot-starter 和 Redis 的相关依赖:

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
  <version>2.2.7</version>
</dependency>

确保依赖的版本是最新的,可以去 Maven Central 查询最新版本。

2. 配置 Redis

在 application.properties 或 application.yml 文件中配置 Redis 的连接信息:

application.properties:

properties 复制代码
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=yourpassword

application.yml:

yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379
    password: yourpassword

3. 使用分布式锁

lock4j-redis-template-spring-boot-starter 提供了简单的注解 @Lock4j,你可以在需要加锁的方法上使用该注解。下面是一个示例:

java 复制代码
import com.baomidou.lock.annotation.Lock4j;
import org.springframework.stereotype.Service;

@Service
public class YourService {

    @Lock4j(keys = {"#arg1", "#arg2"})
    public void yourMethod(String arg1, String arg2) {
        // 需要加锁的业务逻辑
        System.out.println("Method is locked with args: " + arg1 + ", " + arg2);
        // 模拟业务处理
        try {
            Thread.sleep(5000);  // 假设业务逻辑处理需要 5 秒
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

在上述示例中,yourMethod 方法在执行时会获取一个分布式锁,锁的 key 是由方法参数 arg1 和 arg2 的值组合而成。

4. 自定义锁配置

你还可以自定义锁的配置,比如锁的过期时间和获取锁的超时时间等。这些配置可以通过在 application.properties 或 application.yml 文件中进行设置:

application.properties:

properties 复制代码
lock4j.lock.expire = 30000  # 锁的过期时间,单位毫秒
lock4j.lock.timeout = 10000 # 获取锁的超时时间,单位毫秒

application.yml:

yaml 复制代码
lock4j:
  lock:
    expire: 30000  # 锁的过期时间,单位毫秒
    timeout: 10000 # 获取锁的超时时间,单位毫秒

5. 处理锁获取失败的情况

在一些情况下,如果获取锁失败,你可能需要处理这种情况。例如,可以抛出一个异常或者进行重试:

java 复制代码
import com.baomidou.lock.annotation.Lock4j;
import com.github.yitter.lock.exception.LockException;
import org.springframework.stereotype.Service;

@Service
public class YourService {

    @Lock4j(keys = {"#arg1", "#arg2"})
    public void yourMethod(String arg1, String arg2) {
        try {
            // 需要加锁的业务逻辑
            System.out.println("Method is locked with args: " + arg1 + ", " + arg2);
            // 模拟业务处理
            Thread.sleep(5000);  // 假设业务逻辑处理需要 5 秒
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (LockException e) {
            System.err.println("Failed to acquire lock: " + e.getMessage());
            // 处理获取锁失败的情况
        }
    }
}

6. 高级用法

除了简单的注解用法,lock4j-redis-template-spring-boot-starter 还支持更高级的用法,例如:

手动管理锁:你可以通过 Lock4j 提供的 API 手动管理锁。

自定义锁策略:你可以实现自己的锁策略来满足特定需求。

7.样例

java 复制代码
import lombok.Data;

@Data
public class CompanyInfo {

    private Long id;
    private Long gg;
}

没加锁:

java 复制代码
 
    @GetMapping("/lockMethod")
    // @Lock4j(keys = {"#companyInfo.gg"}, acquireTimeout = 50000L, expire = 30000L)
    public String lockMethod(@RequestBody CompanyInfo companyInfo) throws InterruptedException {
        Thread.sleep(2000);
        log.info("Current tt companyInfo: " + companyInfo);
        return companyInfo.getGg().toString();
    }

输出结果:

angular17html 复制代码
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-7] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-6] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-9] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-4] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-8] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-3] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.364  INFO 38028 --- [io-54555-exec-5] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.365  INFO 38028 --- [io-54555-exec-2] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.365  INFO 38028 --- [o-54555-exec-10] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:02.365  INFO 38028 --- [io-54555-exec-1] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:03:03.777  INFO 38028 --- [o-54555-exec-20] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.777  INFO 38028 --- [o-54555-exec-11] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.777  INFO 38028 --- [o-54555-exec-13] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.777  INFO 38028 --- [o-54555-exec-16] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.777  INFO 38028 --- [o-54555-exec-14] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.777  INFO 38028 --- [o-54555-exec-15] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.779  INFO 38028 --- [o-54555-exec-18] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.779  INFO 38028 --- [o-54555-exec-19] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.779  INFO 38028 --- [o-54555-exec-12] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:03:03.780  INFO 38028 --- [o-54555-exec-17] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)

结果得知,抢占资源

加了分布式锁:

java 复制代码
    /**
     * 使用分布式锁演示一个方法,其主要目的是为了锁定特定的键。
     *
     * @param companyInfo 包含用于锁键信息的对象。
     * @return 公司GG值的字符串表示。
     * @throws InterruptedException 如果线程被中断。
     */
    @GetMapping("/lockMethod")
    @Lock4j(keys = {"#companyInfo.gg"}, acquireTimeout = 50000L, expire = 30000L)
    public String lockMethod(@RequestBody CompanyInfo companyInfo) throws InterruptedException {
        Thread.sleep(2000);
        log.info("Current tt companyInfo: " + companyInfo);
        return companyInfo.getGg().toString();
    }

使用压测工具apipost并发10次可以测试得到:

angular17html 复制代码
2024-06-26 12:01:02.754  INFO 34972 --- [o-54555-exec-26] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:03.562  INFO 34972 --- [o-54555-exec-32] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:04.841  INFO 34972 --- [io-54555-exec-6] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:05.636  INFO 34972 --- [o-54555-exec-40] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:06.890  INFO 34972 --- [io-54555-exec-4] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:07.712  INFO 34972 --- [o-54555-exec-38] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:08.946  INFO 34972 --- [io-54555-exec-7] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:09.745  INFO 34972 --- [o-54555-exec-39] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:11.015  INFO 34972 --- [o-54555-exec-12] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:11.791  INFO 34972 --- [o-54555-exec-36] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:13.067  INFO 34972 --- [o-54555-exec-30] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:13.862  INFO 34972 --- [o-54555-exec-35] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:15.108  INFO 34972 --- [o-54555-exec-18] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:15.920  INFO 34972 --- [o-54555-exec-33] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:17.157  INFO 34972 --- [o-54555-exec-11] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:17.953  INFO 34972 --- [o-54555-exec-37] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:19.228  INFO 34972 --- [o-54555-exec-16] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:20.008  INFO 34972 --- [o-54555-exec-41] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)
2024-06-26 12:01:21.266  INFO 34972 --- [o-54555-exec-29] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=1111, gg=222)
2024-06-26 12:01:22.111  INFO 34972 --- [o-54555-exec-34] c.r.R.r.controller.RedisDemoController   : Current tt companyInfo: CompanyInfo(id=44, gg=44)

由上面可以得出,加了分布式锁可以解决资源公用的问题。

总结

通过上述步骤,你可以使用 lock4j-redis-template-spring-boot-starter 快速在 Spring Boot 项目中实现基于 Redis 的分布式锁。该 starter 提供了简洁的注解方式,方便开发者在业务逻辑中使用分布式锁。同时,你还可以根据需要进行自定义配置,以满足不同的业务需求。

相关推荐
靖顺1 小时前
【OceanBase 诊断调优】—— packet fly cost too much time 的根因分析
数据库·oceanbase
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
B1nna2 小时前
Redis学习(三)缓存
redis·学习·缓存
yuanbenshidiaos4 小时前
C++--------------树
java·数据库·c++
dengjiayue5 小时前
MySQL 查询大偏移量(LIMIT)问题分析
数据库·mysql
言之。5 小时前
【MySQL】在MySQL中如何定位慢查询?
数据库·mysql
DashVector6 小时前
如何通过HTTP API插入Doc
数据库·人工智能·http·阿里云·向量检索
DashVector6 小时前
如何通过HTTP API分组检索Doc
服务器·数据库·http·数据库开发·数据库架构
Suwg2096 小时前
【MySQL】踩坑笔记——保存带有换行符等特殊字符的数据,需要进行转义保存
数据库·笔记·mysql
2401_857610036 小时前
中文学习系统:成本效益分析与系统优化
java·数据库·学习·架构