首页 > 其他分享 >SpringBoot+Lock4j实现高性能分布式锁

SpringBoot+Lock4j实现高性能分布式锁

时间:2023-02-21 19:15:11浏览次数:51  
标签:return SpringBoot boot processTime Lock4j import public 分布式

1. 简介

  在分布式业务开发中,很多场景都需要添加分布式锁。在具体实践过程中,研发人员都需要自行实现,导致实现方式不统一,代码风格迥异,难以维护。
  在Mybatis-Plus生态中,Lock4j提供了支持redissionredisTemplatezookeeper的分布式锁组件,简单易用,功能强大,扩展性强。
  Gitee地址:https://gitee.com/baomidou/lock4j

2. 简单示例

  以redisTemplate作为分布式锁底层实现编写示例代码。其他两种方案可参考官网文档实现。

  • 创建项目
  • 修改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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.c3stones</groupId>
    <artifactId>spring-boot-lock4j-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
    </parent>

    <dependencies>
        <!--若使用redisTemplate作为分布式锁底层-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  • 配置redis信息
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    
# Lock4j配置
#lock4j:
#  acquire-timeout: 3000 #等待时长。默认值3s,可不设置
#  expire: 30000 #过期时间,防止死锁。默认值30s,可不设置
#  primary-executor: com.baomidou.lock.executor.RedisTemplateLockExecutor #默认redisson>redisTemplate>zookeeper,可不设置
#  lock-key-prefix: lock4j #锁key前缀。默认值lock4j,可不设置
  • 统一响应类
      发生异常时,由全局异常处理类将异常信息格式化为统一响应类输出。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 响应工具类
 *
 * @param <T>
 * @author CL
 */
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    private Boolean code;

    private String msg;

    private T data;

    public static <T> R<T> ok() {
        return restResult(null, Boolean.TRUE, null);
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, Boolean.TRUE, null);
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, Boolean.TRUE, msg);
    }

    public static <T> R<T> failed() {
        return restResult(null, Boolean.FALSE, null);
    }

    public static <T> R<T> failed(String msg) {
        return restResult(null, Boolean.FALSE, msg);
    }

    public static <T> R<T> failed(T data) {
        return restResult(data, Boolean.FALSE, null);
    }

    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, Boolean.FALSE, msg);
    }

    private static <T> R<T> restResult(T data, Boolean code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }

}
  • 全局异常处理类

/**
 * 全局异常处理
 *
 * @author CL
 */
@Log4j2
@RestControllerAdvice
public class WebExceptionAdvice {

    /**
     * 异常处理
     *
     * @param ex 异常
     * @return {@link R}
     */
    @ExceptionHandler(value = Exception.class)
    public R<?> errorHandler(Exception ex) {
        log.error(ex);
        return R.failed(ex.getMessage());
    }

}
  • 自定义分布式锁失败策略(可选)
import com.baomidou.lock.LockFailureStrategy;
import com.baomidou.lock.exception.LockFailureException;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 自定义获取锁失败策略
 *
 * @author CL
 */
@Component
public class MyLockFailureStrategy implements LockFailureStrategy {

    @Override
    public void onLockFailure(String key, Method method, Object[] arguments) {
        throw new LockFailureException("处理中,请稍后");
    }

}
  • 五种测试方法
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.lock.executor.RedisTemplateLockExecutor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 测试Service
 *
 * @author CL
 */
@Service
@RequiredArgsConstructor
public class TestService {

    /**
     * 测试无锁
     *
     * @param processTime 业务处理时间
     * @return {@link Long}
     */
    public Long test(long processTime) {
        try {
            Thread.sleep(processTime * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return processTime;
    }

    /**
     * 测试默认分布式锁
     *
     * @param processTime 业务处理时间
     * @return {@link Long}
     */
    @Lock4j
    public Long test1(long processTime) {
        return test(processTime);
    }

    /**
     * 测试自定义配置分布式锁
     * <p>
     * keys: 锁名称,全局唯一<br/>
     * expire: 过期时间,防止死锁<br/>
     * acquireTimeout: 等待时间,获取不到则执行失败策略<br/>
     * </p>
     *
     * @param processTime 业务处理时间
     * @return {@link Long}
     */
    @Lock4j(keys = {"#processTime"}, expire = 60000, acquireTimeout = 1000)
    public Long test2(long processTime) {
        return test(processTime);
    }

    private final LockTemplate lockTemplate;

    /**
     * 测试手动获取分布式锁
     *
     * @param processTime 业务处理时间
     * @return {@link Long}
     */
    public Long test3(long processTime) {
        // 获取锁
        final LockInfo lockInfo = lockTemplate.lock("custom:" + processTime, 30000L, 2000L, RedisTemplateLockExecutor.class);
        if (null == lockInfo) {
            throw new RuntimeException("处理中,请稍后");
        }

        // 获取锁成功
        try {
            test(processTime);
        } finally {
            //释放锁
            lockTemplate.releaseLock(lockInfo);
        }

        return processTime;
    }

    /**
     * 测试5秒内只能访问1次
     *
     * @param processTime 业务处理时间
     * @return {@link Long}
     */
    @Lock4j(keys = {"#processTime"}, acquireTimeout = 0, expire = 5000, autoRelease = false)
    public Long test4(long processTime) {
        return processTime;
    }

}

  • 测试接口
import com.c3stones.common.R;
import com.c3stones.service.TestService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试Controller
 *
 * @author CL
 */
@RestController
@RequestMapping()
@RequiredArgsConstructor
public class TestController {

    private final TestService testService;

    /**
     * 测试无锁
     *
     * @param processTime 业务处理时间
     * @return {@link R}
     */
    @RequestMapping("/test")
    public R<Long> test(long processTime) {
        return R.ok(testService.test(processTime));
    }

    /**
     * 测试默认分布式锁
     *
     * @param processTime 业务处理时间
     * @return {@link R}
     */
    @RequestMapping("/test1")
    public R<Long> test1(long processTime) {
        return R.ok(testService.test1(processTime));
    }

    /**
     * 测试自定义配置分布式锁
     *
     * @param processTime 业务处理时间
     * @return {@link R}
     */
    @RequestMapping("/test2")
    public R<Long> test2(long processTime) {
        return R.ok(testService.test2(processTime));
    }

    /**
     * 测试手动获取分布式锁
     *
     * @param processTime 业务处理时间
     * @return {@link R}
     */
    @RequestMapping("/test3")
    public R<Long> test3(long processTime) {
        return R.ok(testService.test3(processTime));
    }

    /**
     * 测试5秒内只能访问1次
     *
     * @param processTime 业务处理时间
     * @return {@link R}
     */
    @RequestMapping("/test4")
    public R<Long> test4(long processTime) {
        return R.ok(testService.test4(processTime));
    }

}
  • 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 启动类
 *
 * @author CL
 */
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

3. 测试

  测试方案:手动快速发送两次相同的请求,假设业务处理时长为5秒,查看响应结果是否满足分布式锁。

  • 测试无锁
      相同条件:第一次请求和第二次请求都不会加锁,等待业务处理完成返回结果。

  • 测试默认分布式锁
      相同条件:第一次请求会加锁,等待业务处理完成返回结果并释放锁;第二次请求默认等待3秒获取锁,若获取锁失败则执行失败策略抛出异常。

  • 测试自定义配置分布式锁
      相同条件:第一次请求会加锁,等待业务处理完成返回结果并释放锁;第二次请求自定义等待1秒获取锁,若获取锁失败则执行失败策略抛出异常。

  • 测试手动获取分布式锁
      相同条件:第一次请求会加锁,等待业务处理完成返回结果并释放锁;第二次请求自定义等待2秒获取锁,若获取锁失败则执行失败策略抛出异常。

  • 测试5秒内只能访问1次
      相同条件:第一次请求会加锁,等待业务处理完成返回结果但不释放锁;第二次请求直接获取锁,若获取锁失败则执行失败策略抛出异常,若获取成功执行业务并加锁。

4. 项目地址

  spring-boot-lock4j-demo

标签:return,SpringBoot,boot,processTime,Lock4j,import,public,分布式
From: https://www.cnblogs.com/cao-lei/p/17142070.html

相关文章

  • springboot读取配置信息,环境变量的方法
    前提配置文件一般是值resources目录下的application.properties或application.yml,其中保存着配置信息代码中实现配置注入的方法使用@Value注解@Value("${test.msg}")......
  • SpringBoot多数据源
    重点概念多数据源在配置之后是根据service的实现类上面的注解来确定生效范围的,一个实现类可以根据注解代表一个数据库ServiceImplimportcom.baomidou.dynamic.datasou......
  • Hadoop 及Spark 分布式HA运行环境搭建
    作者:京东物流秦彪工欲善其事必先利其器,在深入学习大数据相关技术之前,先手动从0到1搭建一个属于自己的本地Hadoop和Spark运行环境,对于继续研究大数据生态圈各类技术具有重......
  • Camunda(二)---Springboot引入
    Springboot引入参考:【第三篇】Camunda系列-整合SpringBoot-腾讯云开发者社区-腾讯云(tencent.com) 简单的引入方式访问:https://start.camunda.com解压压缩包,通过......
  • springboot 使用@Async注解实现异步多线程
    1、在启动类中添加注解@SpringBootApplication@EnableAsync//@ImportResource(locations={"classpath:spring/my.xml"})publicclassDemoApplication{publi......
  • Centos7搭建hadoop3.3.4分布式集群
    目录1、背景2、集群规划2.1hdfs集群规划2.2yarn集群规划3、集群搭建步骤3.1安装JDK3.2修改主机名和host映射3.3配置时间同步3.4关闭防火墙3.5配置ssh免密登录3.5.1......
  • Springboot整合JWT封装工具类篇(二)
    前言:这里是将Springboot整合JWT测试篇(一)封装成工具类publicclassJWTUtils{//秘钥自己保管好privatestaticStringSECRET="token!Q@W3e4r";/**......
  • Springboot整合JWT测试篇(一)
    一、pom文件中引入依赖<!--引入jwt--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>......
  • eclipse创建springBoot项目
    创建SpringBoot工程先在eclipse中安装spring-tool-suite插件,然后根据以下步骤可以创建1、新建SpringStarterProject2、Packaging选择jar 3、勾选Web项 4、项目结......
  • SpringBoot集成Tomcat服务
    目录一、Tomcat集成1、依赖层级2、自动化配置二、Tomcat架构三、Tomcat配置1、基础配置2、属性配置类3、配置加载分析四、周期管理方法1、控制类2、核心方法五、参考源码......