Redisson
# Redisson简介
redisson是一个基于Java的Redis客户端库,提供了一个易于使用且功能丰富的接口,用于与Redis进行交互。为使用Redis的数据结构和特性提供了高级抽象。
# 整合SpringBoot
添加依赖
<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.5</version>
</dependency>
1
2
3
4
5
6
2
3
4
5
6
配置属性
spring:
data:
redis:
# 数据库
database: 0
# 主机
host: localhost
# 端口
port: 6379
# 密码
# password:
# 读超时
timeout: 5s
# 连接超时
connect-timeout: 5s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 操作基本数据类型
@Slf4j
@RestController
@RequestMapping(value = "/api/redis")
public class RedisController {
@Resource
private RedissonClient redissonClient;
// 字符串 String
@GetMapping(value = "/str")
public ResponseEntity<?> getStr() {
// 字符串
RBucket<Object> bucket = redissonClient.getBucket("str-key", new StringCodec("utf-8"));
bucket.set("str1");
bucket.expire(Duration.ofMinutes(2)); // 设置2分钟失效
log.info("字符串str-key:{}", bucket.get()); // 字符串str-key:str1
// JSON字符串
RBucket<Object> bucket2 = redissonClient.getBucket("str-key2", new StringCodec("utf-8"));
Student student = new Student("张三");
bucket2.set(JSON.toJSONString(student), Duration.ofSeconds(60)); // 60秒后失效
log.info("JSON字符串str-key2:{}", bucket2.get()); // JSON字符串str-key2:{"name":"张三"}
return ResponseEntity.ok(bucket2.get());
}
// 哈希 Hash
@GetMapping(value = "/hash")
public ResponseEntity<?> getHash() {
RMap<Object, Object> map = redissonClient.getMap("map", new StringCodec("utf-8"));
map.put("name", "mike");
log.info("哈希map:{}", map.get("name")); // 哈希map:mike
return ResponseEntity.ok(map.get("name"));
}
// 列表 List
@GetMapping(value = "/list")
public ResponseEntity<?> getList() {
RList<Object> list = redissonClient.getList("list", new StringCodec("utf-8"));
Student student = new Student("张三");
Student student2 = new Student("李四");
list.add(student);
list.add(student2);
log.info("列表:{}", list.readAll()); // 列表:[RedisController.Student(name=张三), RedisController.Student(name=李四)]
return ResponseEntity.ok(list.readAll());
}
// 集合 Set
@GetMapping(value = "/set")
public ResponseEntity<?> getSet() {
RSet<Object> set = redissonClient.getSet("setKey", new StringCodec("utf-8"));
Student student = new Student("张三");
set.add(student);
log.info("集合:{}", set.readAll()); // 集合:[RedisController.Student(name=张三)]
return ResponseEntity.ok(set.readAll());
}
// 有序集合 ZSet
@GetMapping(value = "/zset")
public ResponseEntity<?> getZset() {
RScoredSortedSet<Object> sortedSet = redissonClient.getScoredSortedSet("zSetKey", new StringCodec("utf-8"));
Student student = new Student("张三");
Student student2 = new Student("李四");
sortedSet.add(3.0, student);
sortedSet.add(2.0, student2);
log.info("有序集合:{}", sortedSet.readAll()); // 有序集合:[RedisController.Student(name=李四), RedisController.Student(name=张三)]
return ResponseEntity.ok(sortedSet.readAll());
}
// 学生类
@Data
@AllArgsConstructor
@ToString
static class Student {
// 姓名
private String name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 分布式锁
public class LockDemoServiceImpl{
public void addLock(String id) {
// 加锁
boolean isLocked = tryLock(id);
try {
if (isLocked) {
// 业务处理逻辑
} else {
throw new RuntimeException("系统繁忙,请稍后再试");
}
} finally {
// 释放锁
unlock(isLocked, id);
}
}
/**
* 尝试获取分布式锁
*/
private boolean tryLock(String id) {
String lockKey = "business:lock:" + id;
try {
// 尝试获取锁, 2秒超时
return redissonClient.getLock(lockKey).tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error("尝试获取分布式锁[{}]异常", lockKey, e);
return false;
}
}
/**
* 释放分布式锁
*/
public void unlock(boolean isLocked, String id) {
String lockKey = "business:lock:" + id;
RLock lock = redissonClient.getLock(lockKey);
if (isLocked && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 短信验证码
- 重复发送校验,发送验证码前校验1分钟内是否发送过。
- 判断最大发送次数,是否超出最大发送次数。
- 发送验证码。
public class SMSService {
public void sendSMSCode(String telephone){
// 1. 校验该手机号是否重复发送验证码
String key = redissonClient.getBucket("sms-"+telephone, new StringCodec()).get().toString();
if (StringUtils.isNotBlank(key)) {
throw new RuntimeException("请于一分钟后发送短信");
}
// 2. 校验改手机号发送验证码是否达到最大次数
Integer sendCount = (Integer) redissonClient.getBucket("sms-total-"+telephone, new IntegerCodec()).get();
if(sendCount > 5) {
throw new RuntimeException("当日已超出发送次数,请次日重新获取。");
}
// 3. 发送验证码
String randomCode = RandomStringUtils.randomNumeric(4); // 生成验证码
redissonClient.getBucket("sms-"+telephone, new StringCodec()).set(randomCode, 90, TimeUnit.SECONDS); // 添加验证码到缓存中,90s失效
redissonClient.getAtomicLong("sms-total-"+telephone).getAndIncrement(); // 当日发送验证码次数+1
// 设置总发送次数当日有效
redissonClient.getAtomicLong("sms-total-"+telephone).expireAt(Date.from(LocalDateTime.of(LocalDate.now(), LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant()));
// 调用短信发送服务或放入消息队列中进行消费
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上次更新: 2023/12/04, 10:30:45