参考
- 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://blog.csdn.net/MissLone/article/details/119569393
- 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 压测工具 |
正文
配置
- 引入
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