【转载】Springboot2.x 使用 Redisson 分布式可重入锁

发布时间 2023-12-19 19:21:20作者: 夏秋初

参考

注意

  1. 在单元测试中,测试多线程更新年龄失败,但是并没有抛出错误。 https://blog.csdn.net/MissLone/article/details/119569393
  2. Redisson 可以作为 Redis 客户端使用,而不仅仅是分布式锁一种使用方式。

环境

环境 版本 操作
windows 10
vs code 1.84.2
Spring Boot Extension Pack v0.2.1 vscode插件
Extension Pack for Java v0.25.15 vscode插件
JDK 11
Springboot 2.3.12.RELEASE
mybatis-spring-boot-starter 2.1.4 mvn依赖
redisson-spring-boot-starter 3.25.0 mvn依赖
Apache Maven 3.8.6
Redis 3.0.504 windows 版本,已停更,建议 linux 中使用
Apache 2.4.54 包含 abs 压测工具

正文

配置

  1. 引入 pom.xml 依赖。
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.1.4</version>
</dependency>
  1. 添加 src\main\resources\application.properties 配置,引入redisson 配置文件(单节点与集群配置不一样)。
# 单节点 Redisson 配置
spring.redis.redisson.file=classpath:redisson.yaml
  1. 创建文件src\main\resources\redisson.yaml并写入配置。

https://github.com/redisson/redisson/wiki/2.-Configuration#262-single-instance-yaml-config-format

singleServerConfig:
  idleConnectionTimeout: 10000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  password: null
  subscriptionsPerConnection: 5
  clientName: null
  address: "redis://127.0.0.1:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 24
  connectionPoolSize: 64
  database: 0
  dnsMonitoringInterval: 5000
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.Kryo5Codec> {}
transportMode: "NIO"

代码

  1. src\main\java\com\xiaqiuchu\demo\service\StudentService.java
package com.xiaqiuchu.demo.service;

import javax.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import com.xiaqiuchu.demo.entity.Student;
import com.xiaqiuchu.demo.mapper.StudentMapper;

@Service
public class StudentService {
    @Resource
    StudentMapper studentMaper;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 手动管理缓存(基于分布式锁)
     * @param id
     * @return
     */
    public Student toOld(Integer id){
        String lockName = "StudentServiceToOld" + id;
        RLock lock = redissonClient.getLock(lockName);
        try {
            lock.lock();
            System.out.println("获取到锁");
            Student student = studentMaper.findById(id);
            //
            if(Integer.compare(student.getAge(), 100) > 0){
                Assert.isTrue(false, "超过100岁");
            }
            student.setAge((byte) (student.getAge() + 1));
            studentMaper.updateById(student);
            System.out.println("修改成功"+student.getAge());
            return student;
        } finally {
            lock.unlock();
        }
    }
}

  1. src\main\java\com\xiaqiuchu\demo\controller\StudentController.java
package com.xiaqiuchu.demo.controller;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xiaqiuchu.demo.service.StudentService;
import org.springframework.web.bind.annotation.GetMapping;

@RestController
@RequestMapping("/student")
public class StudentController {
    
    @Resource
    StudentService studentService;
    
    // http://127.0.0.1:8080/student/toOldByThread
    @GetMapping("toOldByThread")
    public String toOldByThread() {

        ExecutorService executorService = Executors.newFixedThreadPool(6);
        for (int i = 0; i < 110; i++) {
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName());
                try {
                    studentService.toOld(1);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
              
            });
        }
        executorService.shutdown();

        return "SUCCESS";
    }

    // http://127.0.0.1:8080/student/toOldByAB
    // ab压测测试 abs -n 110  -c 10 http://127.0.0.1:8080/student/toOldByAB
    @GetMapping("toOldByAB")
    public String toOldByAB() {
        studentService.toOld(1);
        return "SUCCESS";
    }
    
    
}

测试

  1. ab 压测
abs -n 110  -c 10 http://127.0.0.1:8080/student/toOldByAB