Springboot2.x 使用 Redisson 分布式可重入锁
参考
- https://blog.csdn.net/weixin_43749805/article/details/131399516
- https://github.com/redisson/redisson (官方仓库)
- https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter#spring-boot-starter
- https://github.com/redisson/redisson/wiki/2.-Configuration#262-single-instance-yaml-config-format (官方单机配置案例)
- https://blog.csdn.net/atu1111/article/details/106325258
- https://blog.csdn.net/MissLone/article/details/119569393
- https://zhuanlan.zhihu.com/p/526578310
- https://www.bookstack.cn/read/redisson-doc-cn/distributed_locks.md#81-lock
- https://zhuanlan.zhihu.com/p/264846738
- https://www.cnblogs.com/cxygg/p/15998082.html
- https://www.cnblogs.com/jackson0714/p/redisson.html
- https://www.cnblogs.com/ssskkk/p/16279323.html
- https://blog.csdn.net/qq_43323776/article/details/82939005
注意
- 在单元测试中,测试多线程更新年龄失败,但是并没有抛出错误。 https://blog.csdn.net/MissLone/article/details/119569393
- Redisson 可以作为 Redis 客户端使用,而不仅仅是分布式锁一种使用方式。
- 看门狗的锁默认超时时间为30s,当服务器宕机后,因为锁的有效期是 30 秒,所以会在 30 秒内自动解锁。
- 可以通过给锁设置过期时间,让其自动解锁,则无需手动解锁。
- 红锁。
- 多个实例争抢锁,使用
getLock
会一直等待锁释放,然后新实例拿到锁再执行。可以使用tryLock
设置获取时间,获取不到就会返回 false,但是不会自动续期。
环境
环境 | 版本 | 操作 |
---|---|---|
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 压测工具 |
正文
配置
- 引入
pom.xml
依赖。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
- 添加
src\main\resources\application.properties
配置,引入redisson 配置文件(单节点与集群配置不一样)。
# 单节点 Redisson 配置
spring.redis.redisson.file=classpath:redisson.yaml
- 创建文件
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"
代码
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();
}
}
}
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";
}
}
测试
- ab 压测
abs -n 110 -c 10 http://127.0.0.1:8080/student/toOldByAB
博 主 :夏秋初
地 址 :https://www.cnblogs.com/xiaqiuchu/p/17914469.html
如果对你有帮助,可以点一下 推荐 或者 关注 吗?会让我的分享变得更有动力~
转载时请带上原文链接,谢谢。
地 址 :https://www.cnblogs.com/xiaqiuchu/p/17914469.html
如果对你有帮助,可以点一下 推荐 或者 关注 吗?会让我的分享变得更有动力~
转载时请带上原文链接,谢谢。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义