12 KiB
后端移除 Redis 改造为内存缓存实施计划
For agentic workers: REQUIRED: Use superpowers:executing-plans to implement this plan in this repository. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 移除后端 Redis 依赖,并在单实例 JVM 内存中承接登录态、验证码、限流、防重提交、参数/字典缓存、在线用户和缓存监控能力。
Architecture: 保留现有 RedisCache 业务入口,底层替换为基于 ConcurrentHashMap 的本地缓存存储,减少对现有业务代码的侵入。限流、在线用户、缓存监控等能力继续沿用现有 key 规则,仅替换存储实现与统计来源。
Tech Stack: Java 8, Spring Boot 2.x, Spring Security, Maven, JUnit 5
文件结构
Create:
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.javaruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.javaruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.javaruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.javaruoyi-common/src/test/java/com/ruoyi/common/core/redis/RedisCacheTest.javaruoyi-framework/src/test/java/com/ruoyi/framework/aspectj/RateLimiterAspectTest.javaruoyi-framework/src/test/java/com/ruoyi/framework/web/service/TokenServiceLocalCacheTest.javaruoyi-admin/src/test/java/com/ruoyi/web/controller/monitor/CacheControllerTest.java
Modify:
pom.xmlruoyi-common/pom.xmlruoyi-framework/pom.xmlruoyi-admin/pom.xmlruoyi-admin/src/main/resources/application.ymlruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.javaruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.javaruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
Delete:
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.javaruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java
Verify existing behavior against:
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.javaruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.javaruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.javaruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.javaruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.javaruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.javaruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.javaruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.javaruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java
Task 1: 搭建内存缓存底座
Files:
-
Create:
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java -
Create:
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java -
Create:
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java -
Test:
ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java -
Step 1: 先写
InMemoryCacheStore的失败测试
@Test
void shouldExpireEntryAfterTimeout() throws Exception {
InMemoryCacheStore store = new InMemoryCacheStore();
store.set("captcha:1", "1234", 50L, TimeUnit.MILLISECONDS);
Thread.sleep(80L);
assertNull(store.get("captcha:1"));
}
- Step 2: 运行测试确认失败
Run: mvn -pl ruoyi-common -Dtest=InMemoryCacheStoreTest test
Expected: FAIL,提示 InMemoryCacheStore 或相关方法不存在
- Step 3: 实现最小缓存条目与存储类
public final class InMemoryCacheEntry {
private final Object value;
private final Long expireAtMillis;
}
@Component
public class InMemoryCacheStore {
private final ConcurrentHashMap<String, InMemoryCacheEntry> entries = new ConcurrentHashMap<>();
}
- Step 4: 为底座补齐核心能力
实现并覆盖测试:
-
set/get -
set(key, value, timeout, unit) -
hasKey -
delete -
delete(Collection<String>) -
keys(pattern) -
expire/getExpire -
increment -
clear -
snapshot -
Step 5: 运行测试确认通过
Run: mvn -pl ruoyi-common -Dtest=InMemoryCacheStoreTest test
Expected: PASS
- Step 6: 提交这一小步
git add ruoyi-common/src/main/java/com/ruoyi/common/core/cache ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java
git commit -m "新增内存缓存底座"
Task 2: 将 RedisCache 改为内存缓存门面
Files:
-
Modify:
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java -
Test:
ruoyi-common/src/test/java/com/ruoyi/common/core/redis/RedisCacheTest.java -
Step 1: 先写
RedisCache门面测试
@Test
void shouldReadAndDeleteCachedObject() {
RedisCache cache = new RedisCache(new InMemoryCacheStore());
cache.setCacheObject("login_tokens:abc", "value");
assertEquals("value", cache.getCacheObject("login_tokens:abc"));
assertTrue(cache.deleteObject("login_tokens:abc"));
}
- Step 2: 运行测试确认失败
Run: mvn -pl ruoyi-common -Dtest=RedisCacheTest test
Expected: FAIL,提示当前 RedisCache 仍依赖 RedisTemplate
- Step 3: 将
RedisCache改成构造注入InMemoryCacheStore
@Component
public class RedisCache {
private final InMemoryCacheStore cacheStore;
public RedisCache(InMemoryCacheStore cacheStore) {
this.cacheStore = cacheStore;
}
}
- Step 4: 保留原有业务方法并补充监控/限流能力
补齐:
-
setCacheObject/getCacheObject -
setCacheMap/getCacheMap -
setCacheList/getCacheList -
setCacheSet/getCacheSet -
deleteObject -
keys -
increment -
getCacheStats -
clear -
Step 5: 运行测试确认通过
Run: mvn -pl ruoyi-common -Dtest=RedisCacheTest,InMemoryCacheStoreTest test
Expected: PASS
- Step 6: 提交这一小步
git add ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java ruoyi-common/src/test/java/com/ruoyi/common/core/redis/RedisCacheTest.java
git commit -m "改造缓存门面为内存实现"
Task 3: 用内存计数替换限流实现
Files:
-
Modify:
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java -
Test:
ruoyi-framework/src/test/java/com/ruoyi/framework/aspectj/RateLimiterAspectTest.java -
Step 1: 为限流切换先写失败测试
@Test
void shouldRejectRequestWhenCountExceeded() throws Throwable {
RedisCache cache = new RedisCache(new InMemoryCacheStore());
RateLimiterAspect aspect = new RateLimiterAspect(cache);
// 连续触发三次,阈值为2,第三次应抛 ServiceException
}
- Step 2: 运行测试确认失败
Run: mvn -pl ruoyi-framework -am -Dtest=RateLimiterAspectTest test
Expected: FAIL,提示当前 RateLimiterAspect 仍依赖 RedisTemplate/RedisScript
- Step 3: 删除
RedisTemplate和 Lua 脚本依赖
将核心调用改为:
long number = redisCache.increment(combineKey, time, TimeUnit.SECONDS);
if (number > count) {
throw new ServiceException("访问过于频繁,请稍候再试");
}
- Step 4: 运行测试确认通过
Run: mvn -pl ruoyi-framework -am -Dtest=RateLimiterAspectTest test
Expected: PASS
- Step 5: 提交这一小步
git add ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java ruoyi-framework/src/test/java/com/ruoyi/framework/aspectj/RateLimiterAspectTest.java
git commit -m "改造限流为内存计数"
Task 4: 调整缓存监控接口到内存统计
Files:
-
Modify:
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java -
Test:
ruoyi-admin/src/test/java/com/ruoyi/web/controller/monitor/CacheControllerTest.java -
Step 1: 为监控接口写失败测试
@Test
void shouldReturnInMemoryCacheStats() {
// 断言返回 info.cache_type == "IN_MEMORY"
// 断言返回 info.cache_mode == "single-instance"
}
- Step 2: 运行测试确认失败
Run: mvn -pl ruoyi-admin -am -Dtest=CacheControllerTest test
Expected: FAIL,提示当前接口仍返回 Redis 信息结构
- Step 3: 将
CacheController改为依赖RedisCache
实现行为:
-
getInfo()返回内存缓存统计 -
getKeys()通过redisCache.keys(pattern)获取 -
getCacheValue()将对象序列化为字符串 -
clearCacheName()、clearCacheKey()、clearCacheAll()走内存门面 -
Step 4: 运行测试确认通过
Run: mvn -pl ruoyi-admin -am -Dtest=CacheControllerTest test
Expected: PASS
- Step 5: 手工冒烟现有依赖方
检查以下类不需要额外逻辑改造:
TokenServiceSysLoginServiceSysRegisterServiceSysPasswordServiceSameUrlDataInterceptorSysUserOnlineControllerSysConfigServiceImplDictUtils
Expected: 仅继续依赖 RedisCache 即可,无需改 key 规则
- Step 6: 提交这一小步
git add ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java ruoyi-admin/src/test/java/com/ruoyi/web/controller/monitor/CacheControllerTest.java
git commit -m "改造缓存监控为内存统计"
Task 5: 删除 Redis 配置与依赖
Files:
-
Modify:
pom.xml -
Modify:
ruoyi-common/pom.xml -
Modify:
ruoyi-framework/pom.xml -
Modify:
ruoyi-admin/pom.xml -
Modify:
ruoyi-admin/src/main/resources/application.yml -
Delete:
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java -
Delete:
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java -
Step 1: 删除 Maven 中 Redis 相关依赖
删除:
-
spring-boot-starter-data-redis -
commons-pool2 -
Step 2: 删除应用配置中的
spring.redis配置块
保留其它配置项不变,只移除 Redis 相关字段
- Step 3: 删除 Redis 基础设施类
删除:
-
RedisConfig -
FastJson2JsonRedisSerializer -
Step 4: 全量编译验证
Run: mvn clean package -DskipTests
Expected: BUILD SUCCESS
- Step 5: 运行后端相关测试
Run: mvn -pl ruoyi-common,ruoyi-framework,ruoyi-admin -am test
Expected: BUILD SUCCESS
- Step 6: 启动后端做接口冒烟
Run: mvn -pl ruoyi-admin -am spring-boot:run
Expected:
-
应用可正常启动
-
不再尝试连接 Redis
-
登录、验证码、缓存监控接口正常可用
-
Step 7: 停止测试进程
结束 spring-boot:run 启动的 Java 进程,避免遗留后台服务
- Step 8: 提交这一小步
git add pom.xml ruoyi-common/pom.xml ruoyi-framework/pom.xml ruoyi-admin/pom.xml ruoyi-admin/src/main/resources/application.yml ruoyi-framework/src/main/java/com/ruoyi/framework/config
git commit -m "移除Redis依赖与配置"
Task 6: 补充实施记录
Files:
-
Modify:
doc/2026-04-15-后端移除Redis改造为内存缓存实施计划.md -
Create or Modify:
doc/2026-04-15-移除Redis依赖改造为内存缓存后端实施记录.md -
Step 1: 记录最终实际修改文件和偏差
记录内容至少包括:
-
实际变更文件
-
测试命令
-
冒烟结论
-
与计划有无偏差
-
Step 2: 提交文档
git add doc/2026-04-15-后端移除Redis改造为内存缓存实施计划.md doc/2026-04-15-移除Redis依赖改造为内存缓存后端实施记录.md
git commit -m "补充后端实施记录"