@llryiop/avatar-boot-cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/exam-question-generate-api.md +163 -0
- package/package.json +1 -1
- package/src/prompts.js +3 -3
- package/src/transform.js +1 -1
- package/templates/.claude/skills/avatar-boot-starter-feign/README.md +243 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/SKILL.md +47 -219
- package/templates/.claude/skills/avatar-boot-starter-feign/references//345/212/237/350/203/275/350/257/246/350/247/243.md +65 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +75 -0
- package/templates/.claude/skills/avatar-boot-starter-feign/references//351/205/215/347/275/256/345/217/202/350/200/203.md +70 -0
- package/templates/.claude/skills/avatar-boot-starter-job/README.md +437 -0
- package/templates/.claude/skills/avatar-boot-starter-job/SKILL.md +35 -414
- package/templates/.claude/skills/avatar-boot-starter-job/references//345/270/270/350/247/201/351/227/256/351/242/230.md +55 -0
- package/templates/.claude/skills/avatar-boot-starter-job/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +124 -0
- package/templates/.claude/skills/avatar-boot-starter-job/references//347/233/221/346/216/247/346/214/207/346/240/207.md +72 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/README.md +580 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/SKILL.md +36 -560
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/234/200/344/275/263/345/256/236/350/267/265.md +43 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/240/270/345/277/203/345/212/237/350/203/275.md +117 -0
- package/templates/.claude/skills/avatar-boot-starter-kafka/references//351/205/215/347/275/256/345/217/202/350/200/203.md +54 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/README.md +572 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/SKILL.md +40 -550
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/256/236/344/275/223/344/270/216/345/212/237/350/203/275.md +96 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/346/225/260/346/215/256/346/272/220.md +91 -0
- package/templates/.claude/skills/avatar-boot-starter-mysql/references//351/253/230/347/272/247/347/211/271/346/200/247/344/270/216/351/205/215/347/275/256.md +59 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/README.md +901 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/SKILL.md +40 -879
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/212/237/350/203/275/344/275/277/347/224/250.md +134 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +96 -0
- package/templates/.claude/skills/avatar-boot-starter-nacos/references//346/225/205/351/232/234/346/216/222/346/237/245.md +64 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/README.md +594 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/SKILL.md +52 -570
- package/templates/.claude/skills/avatar-boot-starter-oss/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +77 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/references//346/240/270/345/277/203/345/212/237/350/203/275.md +94 -0
- package/templates/.claude/skills/avatar-boot-starter-oss/references//350/247/204/350/214/203/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +61 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/README.md +586 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/SKILL.md +42 -566
- package/templates/.claude/skills/avatar-boot-starter-redis/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +78 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/references//346/225/260/346/215/256/346/223/215/344/275/234.md +111 -0
- package/templates/.claude/skills/avatar-boot-starter-redis/references//351/253/230/347/272/247/345/212/237/350/203/275.md +90 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/README.md +662 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/SKILL.md +48 -640
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//346/240/270/345/277/203/345/212/237/350/203/275.md +101 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/205/215/347/275/256/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +44 -0
- package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/253/230/347/272/247/347/211/271/346/200/247.md +71 -0
- package/templates/.claude/skills/avatar-boot-starter-web/README.md +1007 -0
- package/templates/.claude/skills/avatar-boot-starter-web/SKILL.md +150 -1003
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-LogInfo/346/263/250/350/247/243.md +75 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/345/205/250/345/261/200/345/274/202/345/270/270/345/244/204/347/220/206.md +90 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/214/207/346/240/207/347/233/221/346/216/247.md +74 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/227/245/345/277/227/344/275/223/347/263/273.md +73 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/350/257/267/346/261/202/344/270/212/344/270/213/346/226/207.md +77 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +52 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//346/263/250/346/204/217/344/272/213/351/241/271.md +68 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//350/207/252/345/256/232/344/271/211/346/211/251/345/261/225/346/214/207/345/215/227.md +107 -0
- package/templates/.claude/skills/avatar-boot-starter-web/references//351/205/215/347/275/256/345/217/202/350/200/203.md +107 -0
- package/templates/.claude/skills/crud-generator/SKILL.md +133 -64
- package/templates/.claude/skills/database-design/README.md +207 -0
- package/templates/.claude/skills/database-design/SKILL.md +469 -82
- package/templates/.claude/skills/database-design/references//345/221/275/345/220/215/350/247/204/350/214/203.md +232 -0
- package/templates/.claude/skills/database-design/references//345/255/227/346/256/265/347/261/273/345/236/213/350/247/204/350/214/203.md +400 -0
- package/templates/.claude/skills/database-design/references//347/264/242/345/274/225/350/247/204/350/214/203.md +506 -0
- package/templates/avatar-scaffold-api/pom.xml +0 -5
- package/templates/avatar-scaffold-service/pom.xml +25 -87
- package/templates/avatar-scaffold-service/src/main/resources/application-dev.yaml +3 -5
- package/templates/avatar-scaffold-service/src/main/resources/application-local.yaml +2 -2
- package/templates/pom.xml +9 -18
|
@@ -1,586 +1,62 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: avatar-boot-starter-redis
|
|
3
|
-
description:
|
|
3
|
+
description: Avatar Boot Redis 模块使用指南。当用户询问 Redis 缓存操作、分布式锁、限流器、布隆过滤器、String/Hash/Set/ZSet 数据结构操作,或需要接入 avatar-boot-starter-redis 时触发。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Avatar Boot
|
|
6
|
+
# Avatar Boot Starter Redis 使用指南
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
你是 Avatar Boot Redis 模块的使用顾问与开发助手。
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
- ✅ **Redisson 集成** - 内置 Redisson 3.40.2,提供分布式锁、信号量等高级功能
|
|
12
|
-
- ✅ **Spring Cache 支持** - 无缝集成 Spring Cache 抽象,支持 @Cacheable、@CacheEvict、@CachePut
|
|
13
|
-
- ✅ **连接池管理** - 基于 Lettuce 连接池,支持高并发场景
|
|
14
|
-
- ✅ **序列化可配置** - 支持多种序列化方式,推荐 Jackson2JsonRedisSerializer
|
|
15
|
-
- ✅ **完善的监控** - 集成 Micrometer 指标,便于监控 Redis 连接状态
|
|
10
|
+
## 交互流程(必须遵守)
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
**每次被触发时,先通过 AskUserQuestion 工具询问用户意图:**
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
问题:"您好!我是 Avatar Boot Redis 模块助手,请问您需要哪方面的帮助?"
|
|
15
|
+
选项:
|
|
16
|
+
1. **快速接入** - 添加依赖、@EnableRedis、配置连接
|
|
17
|
+
2. **数据结构操作** - String/Hash/Set/ZSet 操作示例
|
|
18
|
+
3. **缓存操作** - RedisCacheClient 使用、批量删除
|
|
19
|
+
4. **分布式锁** - RedissonLockClient 加锁/解锁
|
|
20
|
+
5. **限流与布隆过滤器** - 令牌桶/滑动窗口限流、布隆过滤器去重
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
**根据用户选择,用 Read 工具按下方「文档读取路由」加载对应文档,然后给出具体指导。**
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
<dependency>
|
|
25
|
-
<groupId>com.iflytek.avatar.boot</groupId>
|
|
26
|
-
<artifactId>avatar-boot-starter-redis</artifactId>
|
|
27
|
-
</dependency>
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
> 版本由 Avatar Boot BOM 统一管理,无需指定 version。
|
|
31
|
-
|
|
32
|
-
### 2. 配置文件
|
|
33
|
-
|
|
34
|
-
在 `application.yml` 中添加 Redis 连接配置:
|
|
35
|
-
|
|
36
|
-
```yaml
|
|
37
|
-
spring:
|
|
38
|
-
data:
|
|
39
|
-
redis:
|
|
40
|
-
host: localhost
|
|
41
|
-
port: 6379
|
|
42
|
-
password: your-password # 无密码可省略
|
|
43
|
-
database: 0
|
|
44
|
-
timeout: 3000ms
|
|
45
|
-
lettuce:
|
|
46
|
-
pool:
|
|
47
|
-
max-active: 16 # 最大连接数(默认 8)
|
|
48
|
-
max-idle: 8 # 最大空闲连接数(默认 8)
|
|
49
|
-
min-idle: 2 # 最小空闲连接数(默认 0)
|
|
50
|
-
max-wait: 3000ms # 获取连接最大等待时间
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### 3. 基本使用
|
|
54
|
-
|
|
55
|
-
注入 `StringRedisTemplate` 或 `RedisTemplate` 即可操作 Redis:
|
|
56
|
-
|
|
57
|
-
```java
|
|
58
|
-
package com.example.service;
|
|
59
|
-
|
|
60
|
-
import lombok.RequiredArgsConstructor;
|
|
61
|
-
import lombok.extern.slf4j.Slf4j;
|
|
62
|
-
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
63
|
-
import org.springframework.stereotype.Service;
|
|
64
|
-
|
|
65
|
-
import java.util.concurrent.TimeUnit;
|
|
66
|
-
|
|
67
|
-
@Slf4j
|
|
68
|
-
@Service
|
|
69
|
-
@RequiredArgsConstructor
|
|
70
|
-
public class CacheService {
|
|
71
|
-
|
|
72
|
-
private final StringRedisTemplate stringRedisTemplate;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 设置缓存(必须设置过期时间)
|
|
76
|
-
*/
|
|
77
|
-
public void setCache(String key, String value, long timeout, TimeUnit unit) {
|
|
78
|
-
stringRedisTemplate.opsForValue().set(key, value, timeout, unit);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 获取缓存
|
|
83
|
-
*/
|
|
84
|
-
public String getCache(String key) {
|
|
85
|
-
return stringRedisTemplate.opsForValue().get(key);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* 删除缓存
|
|
90
|
-
*/
|
|
91
|
-
public Boolean deleteCache(String key) {
|
|
92
|
-
return stringRedisTemplate.delete(key);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Redisson 配置
|
|
98
|
-
|
|
99
|
-
### 单节点模式
|
|
100
|
-
|
|
101
|
-
通过 `application.yml` 配置 Redisson:
|
|
102
|
-
|
|
103
|
-
```yaml
|
|
104
|
-
spring:
|
|
105
|
-
data:
|
|
106
|
-
redis:
|
|
107
|
-
host: localhost
|
|
108
|
-
port: 6379
|
|
109
|
-
password: your-password
|
|
110
|
-
redisson:
|
|
111
|
-
config: |
|
|
112
|
-
singleServerConfig:
|
|
113
|
-
address: "redis://localhost:6379"
|
|
114
|
-
password: "your-password"
|
|
115
|
-
connectionMinimumIdleSize: 4
|
|
116
|
-
connectionPoolSize: 16
|
|
117
|
-
idleConnectionTimeout: 10000
|
|
118
|
-
connectTimeout: 10000
|
|
119
|
-
timeout: 3000
|
|
120
|
-
retryAttempts: 3
|
|
121
|
-
retryInterval: 1500
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### 集群模式
|
|
125
|
-
|
|
126
|
-
```yaml
|
|
127
|
-
spring:
|
|
128
|
-
data:
|
|
129
|
-
redis:
|
|
130
|
-
redisson:
|
|
131
|
-
config: |
|
|
132
|
-
clusterServersConfig:
|
|
133
|
-
nodeAddresses:
|
|
134
|
-
- "redis://node1:6379"
|
|
135
|
-
- "redis://node2:6379"
|
|
136
|
-
- "redis://node3:6379"
|
|
137
|
-
password: "your-password"
|
|
138
|
-
masterConnectionMinimumIdleSize: 4
|
|
139
|
-
masterConnectionPoolSize: 16
|
|
140
|
-
slaveConnectionMinimumIdleSize: 4
|
|
141
|
-
slaveConnectionPoolSize: 16
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
也可以通过独立的 `redisson.yaml` 文件配置:
|
|
145
|
-
|
|
146
|
-
```yaml
|
|
147
|
-
# resources/redisson.yaml
|
|
148
|
-
singleServerConfig:
|
|
149
|
-
address: "redis://${REDIS_HOST:localhost}:${REDIS_PORT:6379}"
|
|
150
|
-
password: "${REDIS_PASSWORD:}"
|
|
151
|
-
connectionMinimumIdleSize: 4
|
|
152
|
-
connectionPoolSize: 16
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
然后在 `application.yml` 中引用:
|
|
156
|
-
|
|
157
|
-
```yaml
|
|
158
|
-
spring:
|
|
159
|
-
data:
|
|
160
|
-
redis:
|
|
161
|
-
redisson:
|
|
162
|
-
file: classpath:redisson.yaml
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## RedisTemplate 使用示例
|
|
166
|
-
|
|
167
|
-
### 自定义 RedisTemplate 序列化
|
|
168
|
-
|
|
169
|
-
```java
|
|
170
|
-
package com.example.config;
|
|
171
|
-
|
|
172
|
-
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
|
173
|
-
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
|
174
|
-
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
175
|
-
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
|
176
|
-
import org.springframework.context.annotation.Bean;
|
|
177
|
-
import org.springframework.context.annotation.Configuration;
|
|
178
|
-
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
|
179
|
-
import org.springframework.data.redis.core.RedisTemplate;
|
|
180
|
-
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
|
181
|
-
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
|
182
|
-
|
|
183
|
-
@Configuration
|
|
184
|
-
public class RedisConfig {
|
|
185
|
-
|
|
186
|
-
@Bean
|
|
187
|
-
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
|
188
|
-
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
|
189
|
-
template.setConnectionFactory(factory);
|
|
190
|
-
|
|
191
|
-
// Key 使用 String 序列化
|
|
192
|
-
StringRedisSerializer stringSerializer = new StringRedisSerializer();
|
|
193
|
-
template.setKeySerializer(stringSerializer);
|
|
194
|
-
template.setHashKeySerializer(stringSerializer);
|
|
195
|
-
|
|
196
|
-
// Value 使用 Jackson 序列化
|
|
197
|
-
ObjectMapper om = new ObjectMapper();
|
|
198
|
-
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
|
199
|
-
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
|
|
200
|
-
ObjectMapper.DefaultTyping.NON_FINAL);
|
|
201
|
-
Jackson2JsonRedisSerializer<Object> jacksonSerializer =
|
|
202
|
-
new Jackson2JsonRedisSerializer<>(om, Object.class);
|
|
203
|
-
|
|
204
|
-
template.setValueSerializer(jacksonSerializer);
|
|
205
|
-
template.setHashValueSerializer(jacksonSerializer);
|
|
206
|
-
template.afterPropertiesSet();
|
|
207
|
-
return template;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### 操作各种数据结构
|
|
213
|
-
|
|
214
|
-
```java
|
|
215
|
-
@Service
|
|
216
|
-
@RequiredArgsConstructor
|
|
217
|
-
public class RedisDataService {
|
|
218
|
-
|
|
219
|
-
private final RedisTemplate<String, Object> redisTemplate;
|
|
220
|
-
|
|
221
|
-
// String 操作
|
|
222
|
-
public void stringOps() {
|
|
223
|
-
redisTemplate.opsForValue().set("avatar:user:123", userObj, 30, TimeUnit.MINUTES);
|
|
224
|
-
Object user = redisTemplate.opsForValue().get("avatar:user:123");
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Hash 操作
|
|
228
|
-
public void hashOps() {
|
|
229
|
-
redisTemplate.opsForHash().put("avatar:user:profile:123", "name", "张三");
|
|
230
|
-
redisTemplate.opsForHash().put("avatar:user:profile:123", "age", 25);
|
|
231
|
-
Object name = redisTemplate.opsForHash().get("avatar:user:profile:123", "name");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// List 操作
|
|
235
|
-
public void listOps() {
|
|
236
|
-
redisTemplate.opsForList().rightPush("avatar:queue:tasks", task);
|
|
237
|
-
Object task = redisTemplate.opsForList().leftPop("avatar:queue:tasks");
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Set 操作
|
|
241
|
-
public void setOps() {
|
|
242
|
-
redisTemplate.opsForSet().add("avatar:user:tags:123", "vip", "active");
|
|
243
|
-
Boolean isMember = redisTemplate.opsForSet().isMember("avatar:user:tags:123", "vip");
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// ZSet(有序集合)操作
|
|
247
|
-
public void zsetOps() {
|
|
248
|
-
redisTemplate.opsForZSet().add("avatar:rank:score", "user1", 100.0);
|
|
249
|
-
Set<Object> top10 = redisTemplate.opsForZSet().reverseRange("avatar:rank:score", 0, 9);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## 分布式锁(Redisson)
|
|
255
|
-
|
|
256
|
-
### 基本锁使用
|
|
257
|
-
|
|
258
|
-
```java
|
|
259
|
-
package com.example.service;
|
|
260
|
-
|
|
261
|
-
import lombok.RequiredArgsConstructor;
|
|
262
|
-
import lombok.extern.slf4j.Slf4j;
|
|
263
|
-
import org.redisson.api.RLock;
|
|
264
|
-
import org.redisson.api.RedissonClient;
|
|
265
|
-
import org.springframework.stereotype.Service;
|
|
266
|
-
|
|
267
|
-
import java.util.concurrent.TimeUnit;
|
|
268
|
-
|
|
269
|
-
@Slf4j
|
|
270
|
-
@Service
|
|
271
|
-
@RequiredArgsConstructor
|
|
272
|
-
public class DistributedLockService {
|
|
273
|
-
|
|
274
|
-
private final RedissonClient redissonClient;
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* 使用分布式锁保护临界区
|
|
278
|
-
*/
|
|
279
|
-
public void executeWithLock(String bizKey, Runnable task) {
|
|
280
|
-
RLock lock = redissonClient.getLock("avatar:lock:" + bizKey);
|
|
281
|
-
boolean acquired = false;
|
|
282
|
-
try {
|
|
283
|
-
// 尝试获取锁:等待 5 秒,持有锁 30 秒后自动释放
|
|
284
|
-
acquired = lock.tryLock(5, 30, TimeUnit.SECONDS);
|
|
285
|
-
if (acquired) {
|
|
286
|
-
log.info("获取锁成功: {}", bizKey);
|
|
287
|
-
task.run();
|
|
288
|
-
} else {
|
|
289
|
-
log.warn("获取锁失败: {}", bizKey);
|
|
290
|
-
throw new RuntimeException("获取分布式锁超时");
|
|
291
|
-
}
|
|
292
|
-
} catch (InterruptedException e) {
|
|
293
|
-
Thread.currentThread().interrupt();
|
|
294
|
-
throw new RuntimeException("获取锁被中断", e);
|
|
295
|
-
} finally {
|
|
296
|
-
if (acquired && lock.isHeldByCurrentThread()) {
|
|
297
|
-
lock.unlock();
|
|
298
|
-
log.info("释放锁: {}", bizKey);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* 使用看门狗机制(自动续期)
|
|
305
|
-
* 不指定 leaseTime 时,Redisson 默认启动看门狗,每 10 秒续期一次
|
|
306
|
-
*/
|
|
307
|
-
public void executeWithWatchdog(String bizKey, Runnable task) {
|
|
308
|
-
RLock lock = redissonClient.getLock("avatar:lock:" + bizKey);
|
|
309
|
-
lock.lock(); // 看门狗自动续期
|
|
310
|
-
try {
|
|
311
|
-
task.run();
|
|
312
|
-
} finally {
|
|
313
|
-
if (lock.isHeldByCurrentThread()) {
|
|
314
|
-
lock.unlock();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### 高级锁类型
|
|
322
|
-
|
|
323
|
-
```java
|
|
324
|
-
@Service
|
|
325
|
-
@RequiredArgsConstructor
|
|
326
|
-
public class AdvancedLockService {
|
|
327
|
-
|
|
328
|
-
private final RedissonClient redissonClient;
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* 公平锁 - 按请求顺序获取锁
|
|
332
|
-
*/
|
|
333
|
-
public void fairLock(String bizKey) {
|
|
334
|
-
RLock fairLock = redissonClient.getFairLock("avatar:fairlock:" + bizKey);
|
|
335
|
-
fairLock.lock();
|
|
336
|
-
try {
|
|
337
|
-
// 业务逻辑
|
|
338
|
-
} finally {
|
|
339
|
-
fairLock.unlock();
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* 读写锁 - 读多写少场景
|
|
345
|
-
*/
|
|
346
|
-
public void readWriteLock(String bizKey) {
|
|
347
|
-
RReadWriteLock rwLock = redissonClient.getReadWriteLock("avatar:rwlock:" + bizKey);
|
|
348
|
-
|
|
349
|
-
// 读锁(共享)
|
|
350
|
-
rwLock.readLock().lock();
|
|
351
|
-
try {
|
|
352
|
-
// 读操作
|
|
353
|
-
} finally {
|
|
354
|
-
rwLock.readLock().unlock();
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// 写锁(独占)
|
|
358
|
-
rwLock.writeLock().lock();
|
|
359
|
-
try {
|
|
360
|
-
// 写操作
|
|
361
|
-
} finally {
|
|
362
|
-
rwLock.writeLock().unlock();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## Spring Cache 集成
|
|
369
|
-
|
|
370
|
-
### 启用缓存
|
|
371
|
-
|
|
372
|
-
```java
|
|
373
|
-
package com.example;
|
|
374
|
-
|
|
375
|
-
import org.springframework.boot.SpringApplication;
|
|
376
|
-
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
377
|
-
import org.springframework.cache.annotation.EnableCaching;
|
|
378
|
-
|
|
379
|
-
@EnableCaching
|
|
380
|
-
@SpringBootApplication
|
|
381
|
-
public class Application {
|
|
382
|
-
public static void main(String[] args) {
|
|
383
|
-
SpringApplication.run(Application.class, args);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### 使用缓存注解
|
|
389
|
-
|
|
390
|
-
```java
|
|
391
|
-
package com.example.service;
|
|
392
|
-
|
|
393
|
-
import com.example.entity.User;
|
|
394
|
-
import lombok.RequiredArgsConstructor;
|
|
395
|
-
import org.springframework.cache.annotation.CacheEvict;
|
|
396
|
-
import org.springframework.cache.annotation.CachePut;
|
|
397
|
-
import org.springframework.cache.annotation.Cacheable;
|
|
398
|
-
import org.springframework.stereotype.Service;
|
|
399
|
-
|
|
400
|
-
@Service
|
|
401
|
-
@RequiredArgsConstructor
|
|
402
|
-
public class UserService {
|
|
403
|
-
|
|
404
|
-
private final UserMapper userMapper;
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* 查询时缓存结果
|
|
408
|
-
* 缓存 Key: user::123
|
|
409
|
-
*/
|
|
410
|
-
@Cacheable(cacheNames = "user", key = "#id", unless = "#result == null")
|
|
411
|
-
public User getById(Long id) {
|
|
412
|
-
return userMapper.selectById(id);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* 更新时同步更新缓存
|
|
417
|
-
*/
|
|
418
|
-
@CachePut(cacheNames = "user", key = "#user.id")
|
|
419
|
-
public User update(User user) {
|
|
420
|
-
userMapper.updateById(user);
|
|
421
|
-
return user;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* 删除时清除缓存
|
|
426
|
-
*/
|
|
427
|
-
@CacheEvict(cacheNames = "user", key = "#id")
|
|
428
|
-
public void deleteById(Long id) {
|
|
429
|
-
userMapper.deleteById(id);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* 清除某个缓存空间的所有数据
|
|
434
|
-
*/
|
|
435
|
-
@CacheEvict(cacheNames = "user", allEntries = true)
|
|
436
|
-
public void clearUserCache() {
|
|
437
|
-
// 清空 user 缓存空间
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### 配置 Redis 缓存管理器
|
|
443
|
-
|
|
444
|
-
```yaml
|
|
445
|
-
spring:
|
|
446
|
-
cache:
|
|
447
|
-
type: redis
|
|
448
|
-
redis:
|
|
449
|
-
time-to-live: 1800000 # 默认 TTL: 30 分钟(毫秒)
|
|
450
|
-
key-prefix: "avatar:" # Key 前缀
|
|
451
|
-
use-key-prefix: true
|
|
452
|
-
cache-null-values: false # 不缓存 null 值
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
## Key 命名规范
|
|
456
|
-
|
|
457
|
-
**必须遵循统一的 Key 命名规范:**
|
|
458
|
-
|
|
459
|
-
```
|
|
460
|
-
{appName}:{module}:{bizKey}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
示例:
|
|
464
|
-
|
|
465
|
-
| 场景 | Key 格式 | 示例 |
|
|
466
|
-
|------|---------|------|
|
|
467
|
-
| 用户缓存 | `avatar:user:{userId}` | `avatar:user:123` |
|
|
468
|
-
| 用户 Session | `avatar:session:{sessionId}` | `avatar:session:abc-def-123` |
|
|
469
|
-
| 分布式锁 | `avatar:lock:{bizKey}` | `avatar:lock:order-create-456` |
|
|
470
|
-
| 接口限流 | `avatar:ratelimit:{api}:{clientIp}` | `avatar:ratelimit:/api/sms:10.0.0.1` |
|
|
471
|
-
| 排行榜 | `avatar:rank:{type}` | `avatar:rank:daily-score` |
|
|
472
|
-
|
|
473
|
-
**规范要求:**
|
|
474
|
-
|
|
475
|
-
1. **必须使用冒号分隔**,不要使用下划线或点号
|
|
476
|
-
2. **必须包含应用名前缀**,避免多应用共用 Redis 时 Key 冲突
|
|
477
|
-
3. **必须设置 TTL**,禁止使用永不过期的 Key(防止内存泄漏)
|
|
478
|
-
4. **Key 长度控制**,建议不超过 128 字符
|
|
479
|
-
|
|
480
|
-
## 序列化方式选择
|
|
481
|
-
|
|
482
|
-
| 序列化方式 | 适用场景 | 优点 | 缺点 |
|
|
483
|
-
|-----------|---------|------|------|
|
|
484
|
-
| StringRedisSerializer | 简单字符串 | 可读性好 | 只能存字符串 |
|
|
485
|
-
| Jackson2JsonRedisSerializer | 对象缓存(推荐) | 可读性好、跨语言 | 需要类型信息 |
|
|
486
|
-
| GenericJackson2JsonRedisSerializer | 通用对象 | 自动携带类型信息 | JSON 体积较大 |
|
|
487
|
-
| JdkSerializationRedisSerializer | 默认(不推荐) | 无需配置 | 不可读、跨语言困难 |
|
|
488
|
-
|
|
489
|
-
**推荐方案:**
|
|
490
|
-
- Key 序列化:`StringRedisSerializer`
|
|
491
|
-
- Value 序列化:`Jackson2JsonRedisSerializer`
|
|
492
|
-
- Hash Key 序列化:`StringRedisSerializer`
|
|
493
|
-
- Hash Value 序列化:`Jackson2JsonRedisSerializer`
|
|
494
|
-
|
|
495
|
-
## 最佳实践
|
|
496
|
-
|
|
497
|
-
### 1. 缓存设计
|
|
498
|
-
|
|
499
|
-
- **必须设置 TTL**:所有 Key 必须设置过期时间,禁止永久 Key
|
|
500
|
-
- **缓存穿透防护**:对不存在的数据缓存空值(短 TTL),或使用布隆过滤器
|
|
501
|
-
- **缓存击穿防护**:热点 Key 使用分布式锁重建缓存
|
|
502
|
-
- **缓存雪崩防护**:TTL 加随机偏移量,避免大批 Key 同时过期
|
|
503
|
-
|
|
504
|
-
### 2. 分布式锁
|
|
505
|
-
|
|
506
|
-
- **必须设置超时时间**:防止死锁,推荐 `tryLock(waitTime, leaseTime, unit)`
|
|
507
|
-
- **使用 try-finally**:确保锁一定会被释放
|
|
508
|
-
- **检查锁持有者**:释放前检查 `lock.isHeldByCurrentThread()`
|
|
509
|
-
- **合理设置 leaseTime**:业务执行时间 + 安全余量
|
|
510
|
-
|
|
511
|
-
### 3. 连接池
|
|
512
|
-
|
|
513
|
-
- **合理配置连接数**:根据并发量设置 `max-active`,建议 CPU 核数 * 2
|
|
514
|
-
- **设置最小空闲**:`min-idle` 设为 2-4,避免冷启动延迟
|
|
515
|
-
- **连接超时**:`max-wait` 建议 3-5 秒,不宜过长
|
|
516
|
-
|
|
517
|
-
### 4. 数据一致性
|
|
518
|
-
|
|
519
|
-
- **延迟双删策略**:先删缓存 -> 更新数据库 -> 延迟再删缓存
|
|
520
|
-
- **最终一致性**:通过消息队列异步更新缓存
|
|
521
|
-
- **读写锁**:对一致性要求高的场景使用 Redisson 读写锁
|
|
522
|
-
|
|
523
|
-
## 常见问题
|
|
524
|
-
|
|
525
|
-
### 1. Connection refused(连接被拒绝)
|
|
526
|
-
|
|
527
|
-
**原因**:Redis 服务未启动或配置错误
|
|
528
|
-
|
|
529
|
-
**解决**:
|
|
530
|
-
- 检查 Redis 服务是否启动:`redis-cli ping`
|
|
531
|
-
- 检查 `spring.data.redis.host` 和 `port` 配置是否正确
|
|
532
|
-
- 检查防火墙是否允许访问 Redis 端口
|
|
533
|
-
- 检查 Redis `bind` 配置和 `protected-mode`
|
|
534
|
-
|
|
535
|
-
### 2. Timeout(连接超时)
|
|
536
|
-
|
|
537
|
-
**原因**:连接池耗尽或网络延迟
|
|
538
|
-
|
|
539
|
-
**解决**:
|
|
540
|
-
- 增大连接池 `max-active` 配置
|
|
541
|
-
- 检查是否存在慢查询命令(如 KEYS *),使用 `redis-cli slowlog get` 查看
|
|
542
|
-
- 检查 Redis 内存使用情况:`redis-cli info memory`
|
|
543
|
-
- 调整 `timeout` 和 `max-wait` 配置
|
|
544
|
-
|
|
545
|
-
### 3. 序列化异常(SerializationException)
|
|
546
|
-
|
|
547
|
-
**原因**:序列化/反序列化不匹配
|
|
548
|
-
|
|
549
|
-
**解决**:
|
|
550
|
-
- 确保 RedisTemplate 的序列化器配置一致
|
|
551
|
-
- 使用 Jackson2JsonRedisSerializer 时确保对象有默认构造函数
|
|
552
|
-
- 检查类路径变更导致的反序列化失败
|
|
553
|
-
- 清理旧序列化格式的缓存数据
|
|
24
|
+
---
|
|
554
25
|
|
|
555
|
-
|
|
26
|
+
## 行为准则
|
|
556
27
|
|
|
557
|
-
|
|
28
|
+
1. **回答要具体**:引用具体的 Client 类名、方法名
|
|
29
|
+
2. **主动提醒**:
|
|
30
|
+
- 需要 `@EnableRedis` 注解才能启用模块
|
|
31
|
+
- 分布式锁使用完毕必须释放,建议 try-finally 或回调方式
|
|
32
|
+
- Key 命名规范:`业务:类型:ID`
|
|
33
|
+
3. **使用中文回答**
|
|
558
34
|
|
|
559
|
-
|
|
560
|
-
- 使用 try-finally 确保锁释放
|
|
561
|
-
- 设置合理的 leaseTime 作为兜底
|
|
562
|
-
- 检查是否在异步线程中释放锁(锁绑定线程)
|
|
563
|
-
- 使用 Redisson 看门狗机制自动续期
|
|
35
|
+
---
|
|
564
36
|
|
|
565
|
-
|
|
37
|
+
## 文档读取路由
|
|
566
38
|
|
|
567
|
-
|
|
39
|
+
> 所有路径相对于 skill 目录 `docs/skills/avatar-boot-starter-redis-skill/`
|
|
568
40
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
41
|
+
| 用户需求 | 需读取的文件 |
|
|
42
|
+
|:--|:--|
|
|
43
|
+
| 快速接入 / 配置说明 | `references/快速接入与配置.md` |
|
|
44
|
+
| 数据结构操作 / 缓存操作 | `references/数据操作.md` |
|
|
45
|
+
| 分布式锁 / 限流 / 布隆过滤器 | `references/高级功能.md` |
|
|
574
46
|
|
|
575
|
-
|
|
47
|
+
---
|
|
576
48
|
|
|
577
|
-
|
|
578
|
-
- Spring Data Redis: 由 Spring Boot 3.5.3 BOM 管理
|
|
579
|
-
- Lettuce: 由 Spring Boot 3.5.3 BOM 管理
|
|
580
|
-
- Java: 21
|
|
49
|
+
## 通用参考信息
|
|
581
50
|
|
|
582
|
-
|
|
51
|
+
### 核心 Client 速查
|
|
583
52
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
53
|
+
| Client 类 | 用途 |
|
|
54
|
+
|:--|:--|
|
|
55
|
+
| `RedisStringClient` | String 操作(set/get/increment/multiSet) |
|
|
56
|
+
| `RedisHashClient` | Hash 操作(put/get/getAll/increment) |
|
|
57
|
+
| `RedisSetClient` | Set 操作(add/members/isMember/remove) |
|
|
58
|
+
| `RedisZSetClient` | ZSet 操作(add/reverseRange/score/incrementScore) |
|
|
59
|
+
| `RedisCacheClient` | 缓存操作(set/get/delete/deletePattern) |
|
|
60
|
+
| `RedissonLockClient` | 分布式锁(lock/tryLock/unlock) |
|
|
61
|
+
| `RedisRateLimiterClient` | 限流(tryAcquire/slidingWindowLimit) |
|
|
62
|
+
| `RedisBloomFilterClient` | 布隆过滤器(init/add/contains) |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Redis 快速接入与配置
|
|
2
|
+
|
|
3
|
+
## 1. 添加依赖
|
|
4
|
+
|
|
5
|
+
```xml
|
|
6
|
+
<dependency>
|
|
7
|
+
<groupId>com.iflytek.avatar.boot</groupId>
|
|
8
|
+
<artifactId>avatar-boot-starter-redis</artifactId>
|
|
9
|
+
</dependency>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 2. 启用 Redis 模块
|
|
13
|
+
|
|
14
|
+
```java
|
|
15
|
+
@SpringBootApplication
|
|
16
|
+
@EnableRedis
|
|
17
|
+
public class Application {
|
|
18
|
+
public static void main(String[] args) {
|
|
19
|
+
SpringApplication.run(Application.class, args);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 3. 配置连接
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
spring:
|
|
28
|
+
data:
|
|
29
|
+
redis:
|
|
30
|
+
host: 127.0.0.1
|
|
31
|
+
port: 6379
|
|
32
|
+
password: ${REDIS_PASSWORD}
|
|
33
|
+
database: 0
|
|
34
|
+
timeout: 3000ms
|
|
35
|
+
lettuce:
|
|
36
|
+
pool:
|
|
37
|
+
max-active: 8
|
|
38
|
+
max-idle: 8
|
|
39
|
+
min-idle: 0
|
|
40
|
+
max-wait: -1ms
|
|
41
|
+
|
|
42
|
+
# Redisson 配置(分布式锁/限流/布隆过滤器需要)
|
|
43
|
+
redisson:
|
|
44
|
+
single-server-config:
|
|
45
|
+
address: redis://127.0.0.1:6379
|
|
46
|
+
password: ${REDIS_PASSWORD}
|
|
47
|
+
database: 0
|
|
48
|
+
connection-pool-size: 64
|
|
49
|
+
connection-minimum-idle-size: 32
|
|
50
|
+
timeout: 3000
|
|
51
|
+
connect-timeout: 10000
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 配置说明
|
|
55
|
+
|
|
56
|
+
### Spring Data Redis
|
|
57
|
+
|
|
58
|
+
| 配置项 | 说明 | 默认值 |
|
|
59
|
+
|:--|:--|:--|
|
|
60
|
+
| `spring.data.redis.host` | Redis 主机 | `localhost` |
|
|
61
|
+
| `spring.data.redis.port` | Redis 端口 | `6379` |
|
|
62
|
+
| `spring.data.redis.database` | 数据库索引 | `0` |
|
|
63
|
+
| `spring.data.redis.timeout` | 命令超时时间 | `3000ms` |
|
|
64
|
+
| `lettuce.pool.max-active` | 最大连接数 | `8` |
|
|
65
|
+
|
|
66
|
+
### Redisson
|
|
67
|
+
|
|
68
|
+
| 配置项 | 说明 | 默认值 |
|
|
69
|
+
|:--|:--|:--|
|
|
70
|
+
| `redisson.single-server-config.address` | Redis 地址 | `redis://localhost:6379` |
|
|
71
|
+
| `redisson.single-server-config.connection-pool-size` | 连接池大小 | `64` |
|
|
72
|
+
| `redisson.single-server-config.timeout` | 命令超时(ms) | `3000` |
|
|
73
|
+
|
|
74
|
+
## 注意事项
|
|
75
|
+
|
|
76
|
+
- 需要 `@EnableRedis` 注解才能启用模块
|
|
77
|
+
- 分布式锁、限流、布隆过滤器需要同时配置 `redisson.*`
|
|
78
|
+
- RedisTemplate 默认使用 JSON 序列化,确保对象可序列化
|