From fbd4cfa83c2297486c27ca54954c8b1fe4a3cc07 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Mon, 29 Dec 2025 16:36:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor(integration):=20=E5=B0=86=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E7=BC=93=E5=AD=98=E4=BB=8ERedis=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=88=B0Caffeine=E5=86=85=E5=AD=98=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除RedisTemplate依赖,改用Caffeine作为缓存实现 - 添加缓存互斥锁机制,避免并发请求打崩下游服务 - 统一缓存策略:有缓存直接返回,无缓存调用远程并缓存结果 - 调整缓存TTL配置,从天单位改为分钟单位 - 更新缓存统计信息结构,TTL字段从天改为分钟 - 优化批量清除缓存逻辑,使用流式过滤处理 - 简化缓存操作API,移除无返回值的执行方法 --- pom.xml | 6 + .../com/ycwl/basic/biz/FaceStatusManager.java | 142 +++---- .../service/IntegrationFallbackService.java | 375 ++++-------------- 3 files changed, 138 insertions(+), 385 deletions(-) diff --git a/pom.xml b/pom.xml index 42dfe959..2de00690 100644 --- a/pom.xml +++ b/pom.xml @@ -286,6 +286,12 @@ spring-kafka + + + com.github.ben-manes.caffeine + caffeine + + org.apache.poi diff --git a/src/main/java/com/ycwl/basic/biz/FaceStatusManager.java b/src/main/java/com/ycwl/basic/biz/FaceStatusManager.java index f8d4103a..ae600bb8 100644 --- a/src/main/java/com/ycwl/basic/biz/FaceStatusManager.java +++ b/src/main/java/com/ycwl/basic/biz/FaceStatusManager.java @@ -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 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 faceNoPieceUpdateCache; /** - * 人脸模板渲染状态缓存Key - * 格式: face:status:render:{faceId}:{templateId} - * 过期时间: 永久(或根据业务需要设置) + * 人脸模板渲染状态缓存 */ - private static final String FACE_TEMPLATE_RENDER_KEY = "face:status:render:%s:%s"; + private final Cache templateRenderCache; - /** - * 默认过期时间:1天 - */ - private static final long DEFAULT_EXPIRE_SECONDS = 86400L; - - @Autowired - private RedisTemplate 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); } } } diff --git a/src/main/java/com/ycwl/basic/integration/common/service/IntegrationFallbackService.java b/src/main/java/com/ycwl/basic/integration/common/service/IntegrationFallbackService.java index dc295762..bbd4cc86 100644 --- a/src/main/java/com/ycwl/basic/integration/common/service/IntegrationFallbackService.java +++ b/src/main/java/com/ycwl/basic/integration/common/service/IntegrationFallbackService.java @@ -1,371 +1,153 @@ package com.ycwl.basic.integration.common.service; import com.fasterxml.jackson.core.type.TypeReference; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.ycwl.basic.integration.common.config.IntegrationProperties; import com.ycwl.basic.utils.JacksonUtil; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; -import java.util.Set; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * 集成服务通用失败降级处理 * 提供统一的降级策略,支持所有微服务集成 + * 使用 Caffeine 内存缓存,缓存命中时直接返回避免打崩下游服务 */ @Slf4j @Service -@RequiredArgsConstructor public class IntegrationFallbackService { - - private final RedisTemplate redisTemplate; + private final IntegrationProperties integrationProperties; - - // 默认降级缓存配置 + private final Cache fallbackCache; + private static final String DEFAULT_FALLBACK_PREFIX = "integration:fallback:"; - private static final long DEFAULT_FALLBACK_TTL_DAYS = 1; // 7天 - private static final long FALLBACK_CACHE_PREFERRED_MAX_AGE_SECONDS = 60; // 1分钟 - + private static final long CACHE_TTL_MINUTES = 1; + private static final long MAX_CACHE_SIZE = 50000; + + public IntegrationFallbackService(IntegrationProperties integrationProperties) { + this.integrationProperties = integrationProperties; + this.fallbackCache = Caffeine.newBuilder() + .expireAfterWrite(CACHE_TTL_MINUTES, TimeUnit.MINUTES) + .maximumSize(MAX_CACHE_SIZE) + .build(); + } + /** - * 执行操作,失败时降级到缓存结果 - * - * @param serviceName 服务名称 (如: zt-device, zt-scenic) - * @param cacheKey 缓存键 - * @param operation 主要操作 - * @param resultClass 结果类型 - * @param 结果类型 - * @return 操作结果或缓存的结果 + * 执行操作,优先返回缓存结果 + * 策略:有缓存直接返回,无缓存调用远程并缓存结果 + * 同一 cacheKey 有互斥锁,避免并发请求打崩下游服务 */ public T executeWithFallback(String serviceName, String cacheKey, Supplier operation, Class resultClass) { String fullKey = buildFullCacheKey(serviceName, cacheKey); - String cachedValue = null; - boolean isPreferredCache = false; - try { - cachedValue = redisTemplate.opsForValue().get(fullKey); - isPreferredCache = cachedValue != null && isFallbackCachePreferred(serviceName, fullKey); - } catch (Exception e) { - log.warn("[{}] 读取降级缓存失败,将继续尝试远端获取, cacheKey: {}", serviceName, cacheKey, e); - } - if (isPreferredCache) { - T preferredCacheResult = parseFallbackCacheValue(serviceName, cacheKey, cachedValue, resultClass); - if (preferredCacheResult != null) { - log.debug("[{}] 命中优先缓存(<=1分钟),直接返回, cacheKey: {}", serviceName, cacheKey); - return preferredCacheResult; - } - } - - try { + // Caffeine.get() 内置互斥锁:同一 key 只有一个线程执行 loader,其他线程等待 + String cachedValue = fallbackCache.get(fullKey, k -> { + log.debug("[{}] 缓存未命中,调用远程, cacheKey: {}", serviceName, cacheKey); T result = operation.get(); - if (result != null) { - // 操作成功,保存结果用于将来的降级 - storeFallbackCache(serviceName, cacheKey, result); - } - return result; - } catch (Exception e) { - log.warn("[{}] 操作失败,尝试降级到缓存结果, cacheKey: {}", serviceName, cacheKey, e); - T fallbackResult = parseFallbackCacheValue(serviceName, cacheKey, cachedValue, resultClass); - if (fallbackResult == null) { - fallbackResult = getFallbackFromCache(serviceName, cacheKey, resultClass); - } - if (fallbackResult == null) { - log.error("[{}] 操作失败且无缓存数据, cacheKey: {}", serviceName, cacheKey); - throw e; - } - log.info("[{}] 成功从降级缓存获取结果, cacheKey: {}", serviceName, cacheKey); - return fallbackResult; + return result != null ? JacksonUtil.toJSONString(result) : null; + }); + + if (cachedValue == null) { + return null; } + return parseValue(serviceName, cacheKey, cachedValue, resultClass); } /** - * 执行操作,失败时降级到缓存结果(支持TypeReference泛型) - * - * @param serviceName 服务名称 (如: zt-device, zt-scenic) - * @param cacheKey 缓存键 - * @param operation 主要操作 - * @param typeReference 类型引用,用于保留泛型信息 - * @param 结果类型 - * @return 操作结果或缓存的结果 + * 执行操作,优先返回缓存结果(支持TypeReference泛型) + * 同一 cacheKey 有互斥锁,避免并发请求打崩下游服务 */ public T executeWithFallback(String serviceName, String cacheKey, Supplier operation, TypeReference typeReference) { String fullKey = buildFullCacheKey(serviceName, cacheKey); - String cachedValue = null; - boolean isPreferredCache = false; - try { - cachedValue = redisTemplate.opsForValue().get(fullKey); - isPreferredCache = cachedValue != null && isFallbackCachePreferred(serviceName, fullKey); - } catch (Exception e) { - log.warn("[{}] 读取降级缓存失败,将继续尝试远端获取, cacheKey: {}", serviceName, cacheKey, e); - } - if (isPreferredCache) { - T preferredCacheResult = parseFallbackCacheValue(serviceName, cacheKey, cachedValue, typeReference); - if (preferredCacheResult != null) { - log.debug("[{}] 命中优先缓存(<=1分钟),直接返回, cacheKey: {}", serviceName, cacheKey); - return preferredCacheResult; - } - } - - try { + // Caffeine.get() 内置互斥锁:同一 key 只有一个线程执行 loader,其他线程等待 + String cachedValue = fallbackCache.get(fullKey, k -> { + log.debug("[{}] 缓存未命中,调用远程, cacheKey: {}", serviceName, cacheKey); T result = operation.get(); - if (result != null) { - // 操作成功,保存结果用于将来的降级 - storeFallbackCache(serviceName, cacheKey, result); - } - return result; - } catch (Exception e) { - log.warn("[{}] 操作失败,尝试降级到缓存结果, cacheKey: {}", serviceName, cacheKey, e); - T fallbackResult = parseFallbackCacheValue(serviceName, cacheKey, cachedValue, typeReference); - if (fallbackResult == null) { - fallbackResult = getFallbackFromCache(serviceName, cacheKey, typeReference); - } - if (fallbackResult == null) { - log.error("[{}] 操作失败且无缓存数据, cacheKey: {}", serviceName, cacheKey); - throw e; - } - log.info("[{}] 成功从降级缓存获取结果, cacheKey: {}", serviceName, cacheKey); - return fallbackResult; - } - } - - /** - * 执行操作,失败时降级到缓存结果,无返回值版本 - * - * @param serviceName 服务名称 - * @param cacheKey 缓存键 - * @param operation 主要操作 - */ - public void executeWithFallback(String serviceName, String cacheKey, Runnable operation) { - try { - operation.run(); - // 操作成功,记录成功状态 - storeFallbackCache(serviceName, cacheKey + ":success", "true"); - log.debug("[{}] 操作成功,已记录成功状态, cacheKey: {}", serviceName, cacheKey); - } catch (Exception e) { - log.warn("[{}] 操作失败,检查是否有历史成功记录, cacheKey: {}", serviceName, cacheKey, e); - String successRecord = getFallbackFromCache(serviceName, cacheKey + ":success", String.class); - if (successRecord == null) { - log.error("[{}] 操作失败且无历史成功记录, cacheKey: {}", serviceName, cacheKey); - throw e; - } - log.info("[{}] 操作失败但有历史成功记录,忽略此次失败, cacheKey: {}", serviceName, cacheKey); - } - } - - /** - * 存储降级缓存 - */ - private void storeFallbackCache(String serviceName, String cacheKey, Object value) { - try { - String fullKey = buildFullCacheKey(serviceName, cacheKey); - String jsonValue = JacksonUtil.toJSONString(value); - long ttlDays = getFallbackTtl(serviceName); - redisTemplate.opsForValue().set(fullKey, jsonValue, ttlDays, TimeUnit.DAYS); - log.debug("[{}] 存储降级缓存成功, key: {}", serviceName, fullKey); - } catch (Exception e) { - log.warn("[{}] 存储降级缓存失败, cacheKey: {}", serviceName, cacheKey, e); - } - } - - /** - * 从降级缓存获取结果 - */ - private T getFallbackFromCache(String serviceName, String cacheKey, Class resultClass) { - try { - String fullKey = buildFullCacheKey(serviceName, cacheKey); - String cachedValue = redisTemplate.opsForValue().get(fullKey); - if (cachedValue != null) { - log.debug("[{}] 从降级缓存获取结果, key: {}", serviceName, fullKey); - if (resultClass == String.class) { - return resultClass.cast(cachedValue); - } - return JacksonUtil.parseObject(cachedValue, resultClass); - } - } catch (Exception e) { - log.warn("[{}] 从降级缓存获取结果失败, cacheKey: {}", serviceName, cacheKey, e); - } - return null; - } + return result != null ? JacksonUtil.toJSONString(result) : null; + }); - /** - * 从降级缓存获取结果(支持TypeReference泛型) - */ - private T getFallbackFromCache(String serviceName, String cacheKey, TypeReference typeReference) { - try { - String fullKey = buildFullCacheKey(serviceName, cacheKey); - String cachedValue = redisTemplate.opsForValue().get(fullKey); - if (cachedValue != null) { - log.debug("[{}] 从降级缓存获取结果, key: {}", serviceName, fullKey); - return JacksonUtil.parseObject(cachedValue, typeReference); - } - } catch (Exception e) { - log.warn("[{}] 从降级缓存获取结果失败, cacheKey: {}", serviceName, cacheKey, e); - } - return null; - } - - /** - * 判断当前降级缓存是否应该优先使用。 - * 规则:缓存写入后的 1 分钟内优先使用,超过 1 分钟则优先尝试远端;远端失败再降级到缓存。 - */ - private boolean isFallbackCachePreferred(String serviceName, String fullKey) { - try { - Long remainingSeconds = redisTemplate.getExpire(fullKey, TimeUnit.SECONDS); - if (remainingSeconds == null || remainingSeconds < 0) { - return false; - } - - long ttlDays = getFallbackTtl(serviceName); - long expectedTtlSeconds = TimeUnit.DAYS.toSeconds(ttlDays); - if (remainingSeconds > expectedTtlSeconds) { - return false; - } - - long ageSeconds = expectedTtlSeconds - remainingSeconds; - return ageSeconds <= FALLBACK_CACHE_PREFERRED_MAX_AGE_SECONDS; - } catch (Exception e) { - log.warn("[{}] 判断降级缓存有效期失败,视为不优先, key: {}", serviceName, fullKey, e); - return false; - } - } - - private T parseFallbackCacheValue(String serviceName, String cacheKey, String cachedValue, Class resultClass) { if (cachedValue == null) { return null; } + return parseValue(serviceName, cacheKey, cachedValue, typeReference); + } + + private T parseValue(String serviceName, String cacheKey, String value, Class resultClass) { try { - if (resultClass == String.class) { - return resultClass.cast(cachedValue); - } - return JacksonUtil.parseObject(cachedValue, resultClass); + return JacksonUtil.parseObject(value, resultClass); } catch (Exception e) { - log.warn("[{}] 解析降级缓存失败, cacheKey: {}", serviceName, cacheKey, e); + log.warn("[{}] 解析缓存失败, cacheKey: {}", serviceName, cacheKey, e); return null; } } - /** - * 解析降级缓存值(支持TypeReference泛型) - */ - private T parseFallbackCacheValue(String serviceName, String cacheKey, String cachedValue, TypeReference typeReference) { - if (cachedValue == null) { - return null; - } + private T parseValue(String serviceName, String cacheKey, String value, TypeReference typeReference) { try { - return JacksonUtil.parseObject(cachedValue, typeReference); + return JacksonUtil.parseObject(value, typeReference); } catch (Exception e) { - log.warn("[{}] 解析降级缓存失败, cacheKey: {}", serviceName, cacheKey, e); + log.warn("[{}] 解析缓存失败, cacheKey: {}", serviceName, cacheKey, e); return null; } } - - /** - * 清除降级缓存 - * - * @param serviceName 服务名称 - * @param cacheKey 缓存键 - */ + public void clearFallbackCache(String serviceName, String cacheKey) { String fullKey = buildFullCacheKey(serviceName, cacheKey); - redisTemplate.delete(fullKey); - log.debug("[{}] 清除降级缓存, key: {}", serviceName, fullKey); + fallbackCache.invalidate(fullKey); + log.debug("[{}] 清除缓存, key: {}", serviceName, fullKey); } - - /** - * 批量清除服务的所有降级缓存 - * - * @param serviceName 服务名称 - */ + public void clearAllFallbackCache(String serviceName) { - String pattern = buildFullCacheKey(serviceName, "*"); - Set keys = redisTemplate.keys(pattern); - if (keys != null && !keys.isEmpty()) { - redisTemplate.delete(keys); - log.info("[{}] 批量清除降级缓存,共删除 {} 个缓存项", serviceName, keys.size()); + String prefix = buildFullCacheKey(serviceName, ""); + List keysToRemove = fallbackCache.asMap().keySet().stream() + .filter(key -> key.startsWith(prefix)) + .collect(Collectors.toList()); + if (!keysToRemove.isEmpty()) { + fallbackCache.invalidateAll(keysToRemove); + log.info("[{}] 批量清除缓存,共删除 {} 项", serviceName, keysToRemove.size()); } } - - /** - * 检查是否有降级缓存 - * - * @param serviceName 服务名称 - * @param cacheKey 缓存键 - * @return 是否存在降级缓存 - */ + public boolean hasFallbackCache(String serviceName, String cacheKey) { String fullKey = buildFullCacheKey(serviceName, cacheKey); - return Boolean.TRUE.equals(redisTemplate.hasKey(fullKey)); + return fallbackCache.getIfPresent(fullKey) != null; } - - /** - * 获取服务的降级缓存统计信息 - * - * @param serviceName 服务名称 - * @return 缓存统计信息 - */ + public FallbackCacheStats getFallbackCacheStats(String serviceName) { - String pattern = buildFullCacheKey(serviceName, "*"); - Set keys = redisTemplate.keys(pattern); - int totalCount = keys != null ? keys.size() : 0; - + String prefix = buildFullCacheKey(serviceName, ""); + long totalCount = fallbackCache.asMap().keySet().stream() + .filter(key -> key.startsWith(prefix)) + .count(); + return FallbackCacheStats.builder() .serviceName(serviceName) - .totalCacheCount(totalCount) - .cacheKeyPattern(pattern) - .fallbackTtlDays(getFallbackTtl(serviceName)) + .totalCacheCount((int) totalCount) + .cacheKeyPattern(prefix + "*") + .cacheTtlMinutes(CACHE_TTL_MINUTES) .build(); } - - /** - * 构建完整的缓存键 - */ + private String buildFullCacheKey(String serviceName, String cacheKey) { String prefix = getFallbackPrefix(serviceName); return prefix + serviceName + ":" + cacheKey; } - - /** - * 获取服务的降级缓存前缀 - */ + private String getFallbackPrefix(String serviceName) { if (!integrationProperties.getFallback().isEnabled()) { return DEFAULT_FALLBACK_PREFIX; } - - // 获取服务特定的缓存前缀 IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName); if (serviceConfig != null && serviceConfig.getCachePrefix() != null) { return serviceConfig.getCachePrefix(); } - - // 使用全局配置的前缀 return integrationProperties.getFallback().getCachePrefix(); } - - /** - * 获取服务的降级缓存TTL - */ - private long getFallbackTtl(String serviceName) { - if (!integrationProperties.getFallback().isEnabled()) { - return DEFAULT_FALLBACK_TTL_DAYS; - } - - // 获取服务特定的TTL - IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName); - if (serviceConfig != null && serviceConfig.getTtlDays() > 0) { - return serviceConfig.getTtlDays(); - } - - // 使用全局配置的TTL - return integrationProperties.getFallback().getDefaultTtlDays(); - } - - /** - * 获取服务特定的降级配置 - */ + private IntegrationProperties.ServiceFallbackConfig getServiceFallbackConfig(String serviceName) { switch (serviceName.toLowerCase()) { case "zt-scenic": @@ -376,28 +158,21 @@ public class IntegrationFallbackService { return null; } } - - /** - * 检查服务是否启用降级功能 - */ + public boolean isFallbackEnabled(String serviceName) { if (!integrationProperties.getFallback().isEnabled()) { return false; } - IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName); return serviceConfig == null || serviceConfig.isEnabled(); } - - /** - * 降级缓存统计信息 - */ + @lombok.Builder @lombok.Data public static class FallbackCacheStats { private String serviceName; private int totalCacheCount; private String cacheKeyPattern; - private long fallbackTtlDays; + private long cacheTtlMinutes; } }