You've already forked FrameTour-BE
refactor(integration): 将降级缓存从Redis迁移到Caffeine内存缓存
- 移除RedisTemplate依赖,改用Caffeine作为缓存实现 - 添加缓存互斥锁机制,避免并发请求打崩下游服务 - 统一缓存策略:有缓存直接返回,无缓存调用远程并缓存结果 - 调整缓存TTL配置,从天单位改为分钟单位 - 更新缓存统计信息结构,TTL字段从天改为分钟 - 优化批量清除缓存逻辑,使用流式过滤处理 - 简化缓存操作API,移除无返回值的执行方法
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package com.ycwl.basic.biz;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.ycwl.basic.enums.FaceCutStatus;
|
||||
import com.ycwl.basic.enums.FacePieceUpdateStatus;
|
||||
import com.ycwl.basic.enums.TemplateRenderStatus;
|
||||
@@ -7,60 +9,60 @@ import com.ycwl.basic.mapper.TaskMapper;
|
||||
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 人脸状态缓存管理器
|
||||
* 统一管理人脸相关的Redis缓存状态
|
||||
* 统一管理人脸相关的内存缓存状态(使用Caffeine)
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FaceStatusManager {
|
||||
|
||||
/**
|
||||
* 人脸切片状态缓存Key
|
||||
* 格式: face:status:cut:{faceId}
|
||||
* 过期时间: 1天
|
||||
* 默认过期时间:1小时
|
||||
*/
|
||||
private static final String FACE_CUT_STATUS_KEY = "face:status:cut:%s";
|
||||
private static final long DEFAULT_EXPIRE_SECONDS = 3600L;
|
||||
|
||||
/**
|
||||
* 人脸片段更新状态缓存Key(全局)
|
||||
* 格式: face:status:piece:update:{faceId}
|
||||
* 过期时间: 1天
|
||||
* 键存在 = 无新片段,键不存在 = 有新片段
|
||||
* 人脸切片状态缓存
|
||||
*/
|
||||
private static final String FACE_PIECE_UPDATE_KEY = "face:status:piece:update:%s";
|
||||
private final Cache<String, Integer> faceCutStatusCache;
|
||||
|
||||
/**
|
||||
* 人脸模板片段更新状态缓存Key(按模板)
|
||||
* 格式: face:status:piece:update:{faceId}:{templateId}
|
||||
* 过期时间: 1天
|
||||
* 键存在 = 无新片段,键不存在 = 有新片段
|
||||
* 人脸片段更新状态缓存(全局和模板级)
|
||||
* 键存在=无新片段,键不存在=有新片段
|
||||
*/
|
||||
private static final String FACE_TEMPLATE_PIECE_UPDATE_KEY = "face:status:piece:update:%s:%s";
|
||||
private final Cache<String, Boolean> faceNoPieceUpdateCache;
|
||||
|
||||
/**
|
||||
* 人脸模板渲染状态缓存Key
|
||||
* 格式: face:status:render:{faceId}:{templateId}
|
||||
* 过期时间: 永久(或根据业务需要设置)
|
||||
* 人脸模板渲染状态缓存
|
||||
*/
|
||||
private static final String FACE_TEMPLATE_RENDER_KEY = "face:status:render:%s:%s";
|
||||
private final Cache<String, Integer> templateRenderCache;
|
||||
|
||||
/**
|
||||
* 默认过期时间:1天
|
||||
*/
|
||||
private static final long DEFAULT_EXPIRE_SECONDS = 86400L;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private TaskMapper taskMapper;
|
||||
|
||||
public FaceStatusManager() {
|
||||
// 初始化三个独立的缓存实例
|
||||
this.faceCutStatusCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
|
||||
.maximumSize(10000)
|
||||
.build();
|
||||
|
||||
this.faceNoPieceUpdateCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
|
||||
.maximumSize(10000)
|
||||
.build();
|
||||
|
||||
this.templateRenderCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
|
||||
.maximumSize(10000)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ==================== 切片状态相关方法 ====================
|
||||
|
||||
/**
|
||||
@@ -73,8 +75,7 @@ public class FaceStatusManager {
|
||||
log.warn("设置切片状态参数为空: faceId={}, status={}", faceId, status);
|
||||
return;
|
||||
}
|
||||
String key = String.format(FACE_CUT_STATUS_KEY, faceId);
|
||||
redisTemplate.opsForValue().set(key, String.valueOf(status.getCode()), DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||
faceCutStatusCache.put(String.valueOf(faceId), status.getCode());
|
||||
log.debug("设置切片状态: faceId={}, status={}", faceId, status.getDescription());
|
||||
}
|
||||
|
||||
@@ -88,19 +89,12 @@ public class FaceStatusManager {
|
||||
log.warn("获取切片状态参数为空: faceId={}", faceId);
|
||||
return FaceCutStatus.COMPLETED;
|
||||
}
|
||||
String key = String.format(FACE_CUT_STATUS_KEY, faceId);
|
||||
String value = redisTemplate.opsForValue().get(key);
|
||||
if (value == null) {
|
||||
Integer code = faceCutStatusCache.getIfPresent(String.valueOf(faceId));
|
||||
if (code == null) {
|
||||
log.debug("切片状态缓存不存在,返回默认值COMPLETED: faceId={}", faceId);
|
||||
return FaceCutStatus.COMPLETED;
|
||||
}
|
||||
try {
|
||||
int code = Integer.parseInt(value);
|
||||
return FaceCutStatus.fromCodeOrDefault(code, FaceCutStatus.COMPLETED);
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("切片状态值解析失败: faceId={}, value={}", faceId, value, e);
|
||||
return FaceCutStatus.COMPLETED;
|
||||
}
|
||||
return FaceCutStatus.fromCodeOrDefault(code, FaceCutStatus.COMPLETED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,15 +105,14 @@ public class FaceStatusManager {
|
||||
if (faceId == null) {
|
||||
return;
|
||||
}
|
||||
String key = String.format(FACE_CUT_STATUS_KEY, faceId);
|
||||
redisTemplate.delete(key);
|
||||
faceCutStatusCache.invalidate(String.valueOf(faceId));
|
||||
log.debug("删除切片状态缓存: faceId={}", faceId);
|
||||
}
|
||||
|
||||
// ==================== 片段更新状态相关方法 ====================
|
||||
|
||||
/**
|
||||
* 标记无新片段(设置Redis键)
|
||||
* 标记无新片段(设置缓存键)
|
||||
* @param faceId 人脸ID
|
||||
* @param templateId 模板ID(可选,为null时标记全局状态)
|
||||
*/
|
||||
@@ -129,22 +122,19 @@ public class FaceStatusManager {
|
||||
return;
|
||||
}
|
||||
|
||||
String key;
|
||||
if (templateId == null) {
|
||||
// 全局标记:该人脸的所有模板都无新片段
|
||||
key = String.format(FACE_PIECE_UPDATE_KEY, faceId);
|
||||
faceNoPieceUpdateCache.put(String.valueOf(faceId), Boolean.TRUE);
|
||||
log.debug("标记无新片段(全局): faceId={}", faceId);
|
||||
} else {
|
||||
// 模板级标记:该人脸在该模板下无新片段
|
||||
key = String.format(FACE_TEMPLATE_PIECE_UPDATE_KEY, faceId, templateId);
|
||||
faceNoPieceUpdateCache.put(faceId + ":" + templateId, Boolean.TRUE);
|
||||
log.debug("标记无新片段(模板): faceId={}, templateId={}", faceId, templateId);
|
||||
}
|
||||
|
||||
redisTemplate.opsForValue().set(key, "1", DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记有新片段(删除Redis键)
|
||||
* 标记有新片段(删除缓存键)
|
||||
* @param faceId 人脸ID
|
||||
* @param templateId 模板ID(可选,为null时标记全局状态)
|
||||
*/
|
||||
@@ -154,18 +144,15 @@ public class FaceStatusManager {
|
||||
return;
|
||||
}
|
||||
|
||||
String key;
|
||||
if (templateId == null) {
|
||||
// 全局标记:该人脸有新片段
|
||||
key = String.format(FACE_PIECE_UPDATE_KEY, faceId);
|
||||
faceNoPieceUpdateCache.invalidate(String.valueOf(faceId));
|
||||
log.debug("标记有新片段(全局): faceId={}", faceId);
|
||||
} else {
|
||||
// 模板级标记:该人脸在该模板下有新片段
|
||||
key = String.format(FACE_TEMPLATE_PIECE_UPDATE_KEY, faceId, templateId);
|
||||
faceNoPieceUpdateCache.invalidate(faceId + ":" + templateId);
|
||||
log.debug("标记有新片段(模板): faceId={}, templateId={}", faceId, templateId);
|
||||
}
|
||||
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,17 +167,9 @@ public class FaceStatusManager {
|
||||
return FacePieceUpdateStatus.HAS_NEW_PIECES;
|
||||
}
|
||||
|
||||
String key;
|
||||
if (templateId == null) {
|
||||
// 查询全局状态
|
||||
key = String.format(FACE_PIECE_UPDATE_KEY, faceId);
|
||||
} else {
|
||||
// 查询模板级状态
|
||||
key = String.format(FACE_TEMPLATE_PIECE_UPDATE_KEY, faceId, templateId);
|
||||
}
|
||||
|
||||
Boolean exists = redisTemplate.hasKey(key);
|
||||
FacePieceUpdateStatus status = FacePieceUpdateStatus.fromKeyExists(Boolean.TRUE.equals(exists));
|
||||
String key = templateId == null ? String.valueOf(faceId) : faceId + ":" + templateId;
|
||||
boolean exists = faceNoPieceUpdateCache.getIfPresent(key) != null;
|
||||
FacePieceUpdateStatus status = FacePieceUpdateStatus.fromKeyExists(exists);
|
||||
|
||||
if (templateId == null) {
|
||||
log.debug("获取片段更新状态(全局): faceId={}, status={}", faceId, status.getDescription());
|
||||
@@ -248,8 +227,7 @@ public class FaceStatusManager {
|
||||
log.warn("设置模板渲染状态参数为空: faceId={}, templateId={}, status={}", faceId, templateId, status);
|
||||
return;
|
||||
}
|
||||
String key = String.format(FACE_TEMPLATE_RENDER_KEY, faceId, templateId);
|
||||
redisTemplate.opsForValue().set(key, String.valueOf(status.getCode()), DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||
templateRenderCache.put(faceId + ":" + templateId, status.getCode());
|
||||
log.debug("设置模板渲染状态: faceId={}, templateId={}, status={}", faceId, templateId, status.getDescription());
|
||||
}
|
||||
|
||||
@@ -264,11 +242,10 @@ public class FaceStatusManager {
|
||||
log.warn("获取模板渲染状态参数为空: faceId={}, templateId={}", faceId, templateId);
|
||||
return null;
|
||||
}
|
||||
String key = String.format(FACE_TEMPLATE_RENDER_KEY, faceId, templateId);
|
||||
String value = redisTemplate.opsForValue().get(key);
|
||||
if (value == null) {
|
||||
Integer code = templateRenderCache.getIfPresent(faceId + ":" + templateId);
|
||||
if (code == null) {
|
||||
log.debug("模板渲染状态缓存不存在: faceId={}, templateId={}", faceId, templateId);
|
||||
// 查一下
|
||||
// 查询数据库
|
||||
TaskEntity task = taskMapper.listLastFaceTemplateTask(faceId, templateId);
|
||||
if (task == null) {
|
||||
setTemplateRenderStatus(faceId, templateId, TemplateRenderStatus.NONE);
|
||||
@@ -282,13 +259,7 @@ public class FaceStatusManager {
|
||||
}
|
||||
return TemplateRenderStatus.NONE;
|
||||
}
|
||||
try {
|
||||
int code = Integer.parseInt(value);
|
||||
return TemplateRenderStatus.fromCode(code);
|
||||
} catch (Exception e) {
|
||||
log.error("模板渲染状态值解析失败: faceId={}, templateId={}, value={}", faceId, templateId, value, e);
|
||||
return null;
|
||||
}
|
||||
return TemplateRenderStatus.fromCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,8 +271,7 @@ public class FaceStatusManager {
|
||||
if (faceId == null || templateId == null) {
|
||||
return;
|
||||
}
|
||||
String key = String.format(FACE_TEMPLATE_RENDER_KEY, faceId, templateId);
|
||||
redisTemplate.delete(key);
|
||||
templateRenderCache.invalidate(faceId + ":" + templateId);
|
||||
log.debug("删除模板渲染状态缓存: faceId={}, templateId={}", faceId, templateId);
|
||||
}
|
||||
|
||||
@@ -314,11 +284,13 @@ public class FaceStatusManager {
|
||||
if (faceId == null) {
|
||||
return;
|
||||
}
|
||||
String pattern = String.format(FACE_TEMPLATE_RENDER_KEY, faceId, "*");
|
||||
var keys = redisTemplate.keys(pattern);
|
||||
if (keys != null && !keys.isEmpty()) {
|
||||
redisTemplate.delete(keys);
|
||||
log.debug("批量删除模板渲染状态缓存: faceId={}, count={}", faceId, keys.size());
|
||||
String prefix = faceId + ":";
|
||||
long count = templateRenderCache.asMap().keySet().stream()
|
||||
.filter(key -> key.startsWith(prefix))
|
||||
.peek(templateRenderCache::invalidate)
|
||||
.count();
|
||||
if (count > 0) {
|
||||
log.debug("批量删除模板渲染状态缓存: faceId={}, count={}", faceId, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user