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

1. 简介

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

2. 简单示例

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

  • 创建项目SpringBoot + Lock4j实现高性能分布式锁_第1张图片
  • 修改pom.xml


    4.0.0

    com.c3stones
    spring-boot-lock4j-demo
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.7.7
    

    
        
        
            com.baomidou
            lock4j-redis-template-spring-boot-starter
            2.2.4
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.springframework.boot
            spring-boot-starter-web
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

  • 配置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 
 * @author CL
 */
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R implements Serializable {

    private static final long serialVersionUID = 1L;

    private Boolean code;

    private String msg;

    private T data;

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

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

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

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

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

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

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

    private static  R restResult(T data, Boolean code, String msg) {
        R 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);
    }

    /**
     * 测试自定义配置分布式锁
     * 

* keys: 锁名称,全局唯一
* expire: 过期时间,防止死锁
* acquireTimeout: 等待时间,获取不到则执行失败策略
*

* * @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 test(long processTime) {
        return R.ok(testService.test(processTime));
    }

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

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

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

    /**
     * 测试5秒内只能访问1次
     *
     * @param processTime 业务处理时间
     * @return {@link R}
     */
    @RequestMapping("/test4")
    public R 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秒,查看响应结果是否满足分布式锁。

  • 测试无锁
      相同条件:第一次请求和第二次请求都不会加锁,等待业务处理完成返回结果。SpringBoot + Lock4j实现高性能分布式锁_第2张图片SpringBoot + Lock4j实现高性能分布式锁_第3张图片
  • 测试默认分布式锁
      相同条件:第一次请求会加锁,等待业务处理完成返回结果并释放锁;第二次请求默认等待3秒获取锁,若获取锁失败则执行失败策略抛出异常。SpringBoot + Lock4j实现高性能分布式锁_第4张图片SpringBoot + Lock4j实现高性能分布式锁_第5张图片
  • 测试自定义配置分布式锁
      相同条件:第一次请求会加锁,等待业务处理完成返回结果并释放锁;第二次请求自定义等待1秒获取锁,若获取锁失败则执行失败策略抛出异常。SpringBoot + Lock4j实现高性能分布式锁_第6张图片SpringBoot + Lock4j实现高性能分布式锁_第7张图片
  • 测试手动获取分布式锁
      相同条件:第一次请求会加锁,等待业务处理完成返回结果并释放锁;第二次请求自定义等待2秒获取锁,若获取锁失败则执行失败策略抛出异常。SpringBoot + Lock4j实现高性能分布式锁_第8张图片SpringBoot + Lock4j实现高性能分布式锁_第9张图片
  • 测试5秒内只能访问1次
      相同条件:第一次请求会加锁,等待业务处理完成返回结果但不释放锁;第二次请求直接获取锁,若获取锁失败则执行失败策略抛出异常,若获取成功执行业务并加锁。SpringBoot + Lock4j实现高性能分布式锁_第10张图片SpringBoot + Lock4j实现高性能分布式锁_第11张图片
4. 项目地址

  spring-boot-lock4j-demo

你可能感兴趣的:(spring,boot,分布式,java,mybatis,redis)