diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java index 462f16530..45ac5bbcc 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java @@ -83,7 +83,7 @@ public class RepeatSubmitAspect { if (r.getCode() == HttpStatus.SUCCESS) { return; } - RedisUtils.deleteObject(KEY_CACHE.get()); + deleteRepeatKey(); } } finally { KEY_CACHE.remove(); @@ -98,8 +98,18 @@ public class RepeatSubmitAspect { */ @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { - RedisUtils.deleteObject(KEY_CACHE.get()); - KEY_CACHE.remove(); + try { + deleteRepeatKey(); + } finally { + KEY_CACHE.remove(); + } + } + + private void deleteRepeatKey() { + String key = KEY_CACHE.get(); + if (StringUtils.isNotBlank(key)) { + RedisUtils.deleteObject(key); + } } /** diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java index c016e166f..00bca0902 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java @@ -24,6 +24,7 @@ import tools.jackson.databind.module.SimpleModule; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.TimeZone; /** diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java index ebec7861a..d04fb64d5 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java @@ -23,12 +23,12 @@ public class RedissonProperties { /** * 线程池数量,默认值 = 当前处理核数量 * 2 */ - private int threads; + private int threads = Runtime.getRuntime().availableProcessors() * 2; /** * Netty线程池数量,默认值 = 当前处理核数量 * 2 */ - private int nettyThreads; + private int nettyThreads = Runtime.getRuntime().availableProcessors() * 2; /** * 单机服务配置 diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java index 884a92ac4..8368aa3c7 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java @@ -1,6 +1,5 @@ package org.dromara.common.redis.manager; -import org.dromara.common.core.utils.SpringUtils; import org.springframework.cache.Cache; import java.util.concurrent.Callable; @@ -12,21 +11,21 @@ import java.util.concurrent.Callable; */ public class CaffeineCacheDecorator implements Cache { - private static final com.github.benmanes.caffeine.cache.Cache - CAFFEINE = SpringUtils.getBean("caffeine"); - private final String name; private final Cache cache; + private final com.github.benmanes.caffeine.cache.Cache caffeine; /** * 创建带 Caffeine 一级缓存的缓存装饰器。 * * @param name 缓存名称 - * @param cache 被装饰的缓存实例 + * @param cache 被装饰的缓存实例 + * @param caffeine 本地一级缓存实例 */ - public CaffeineCacheDecorator(String name, Cache cache) { + public CaffeineCacheDecorator(String name, Cache cache, com.github.benmanes.caffeine.cache.Cache caffeine) { this.name = name; this.cache = cache; + this.caffeine = caffeine; } /** @@ -67,7 +66,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public ValueWrapper get(Object key) { - Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key)); + Object o = caffeine.get(getUniqueKey(key), k -> cache.get(key)); return (ValueWrapper) o; } @@ -81,7 +80,7 @@ public class CaffeineCacheDecorator implements Cache { @SuppressWarnings("unchecked") @Override public T get(Object key, Class type) { - Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type)); + Object o = caffeine.get(getUniqueKey(key), k -> cache.get(key, type)); return (T) o; } @@ -93,7 +92,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public void put(Object key, Object value) { - CAFFEINE.invalidate(getUniqueKey(key)); + caffeine.invalidate(getUniqueKey(key)); cache.put(key, value); } @@ -106,7 +105,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public ValueWrapper putIfAbsent(Object key, Object value) { - CAFFEINE.invalidate(getUniqueKey(key)); + caffeine.invalidate(getUniqueKey(key)); return cache.putIfAbsent(key, value); } @@ -130,7 +129,7 @@ public class CaffeineCacheDecorator implements Cache { public boolean evictIfPresent(Object key) { boolean b = cache.evictIfPresent(key); if (b) { - CAFFEINE.invalidate(getUniqueKey(key)); + caffeine.invalidate(getUniqueKey(key)); } return b; } @@ -140,7 +139,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public void clear() { - CAFFEINE.invalidateAll(); + clearLocalCache(); cache.clear(); } @@ -151,7 +150,11 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public boolean invalidate() { - return cache.invalidate(); + boolean invalidated = cache.invalidate(); + if (invalidated) { + clearLocalCache(); + } + return invalidated; } /** @@ -164,8 +167,13 @@ public class CaffeineCacheDecorator implements Cache { @SuppressWarnings("unchecked") @Override public T get(Object key, Callable valueLoader) { - Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader)); + Object o = caffeine.get(getUniqueKey(key), k -> cache.get(key, valueLoader)); return (T) o; } + private void clearLocalCache() { + String prefix = name + ":"; + caffeine.asMap().keySet().removeIf(key -> key instanceof String cacheKey && cacheKey.startsWith(prefix)); + } + } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index 1a3939d5a..7e02a7dd2 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -55,10 +55,22 @@ public class PlusSpringCacheManager implements CacheManager { Map configMap = new ConcurrentHashMap<>(); ConcurrentMap instanceMap = new ConcurrentHashMap<>(); + private final com.github.benmanes.caffeine.cache.Cache caffeine; + /** * 创建基于 Redisson 的缓存管理器。 */ public PlusSpringCacheManager() { + this(null); + } + + /** + * 创建基于 Redisson 的缓存管理器。 + * + * @param caffeine 本地一级缓存实例 + */ + public PlusSpringCacheManager(com.github.benmanes.caffeine.cache.Cache caffeine) { + this.caffeine = caffeine; } @@ -110,7 +122,11 @@ public class PlusSpringCacheManager implements CacheManager { * @param config object */ public void setConfig(Map config) { - this.configMap = (Map) config; + if (config == null) { + this.configMap = new ConcurrentHashMap<>(); + return; + } + this.configMap = new ConcurrentHashMap<>((Map) config); } /** @@ -130,11 +146,12 @@ public class PlusSpringCacheManager implements CacheManager { */ @Override public Cache getCache(String name) { + String cacheName = name; // 重写 cacheName 支持多参数 String[] array = StringUtils.delimitedListToStringArray(name, "#"); name = array[0]; - Cache cache = instanceMap.get(name); + Cache cache = instanceMap.get(cacheName); if (cache != null) { return cache; } @@ -142,10 +159,28 @@ public class PlusSpringCacheManager implements CacheManager { return cache; } - CacheConfig config = configMap.get(name); + CacheConfig config = resolveCacheConfig(cacheName, name, array); + + int local = resolveLocal(array); + if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { + return createMap(cacheName, name, config, local); + } + + return createMapCache(cacheName, name, config, local); + } + + private CacheConfig resolveCacheConfig(String cacheName, String name, String[] array) { + CacheConfig config = configMap.get(cacheName); + if (config != null) { + return config; + } + + CacheConfig template = configMap.get(name); + if (template != null) { + config = copyConfig(template); + } if (config == null) { config = createDefaultConfig(); - configMap.put(name, config); } if (array.length > 1) { @@ -157,16 +192,28 @@ public class PlusSpringCacheManager implements CacheManager { if (array.length > 3) { config.setMaxSize(Integer.parseInt(array[3])); } + configMap.put(cacheName, config); + return config; + } + + private int resolveLocal(String[] array) { int local = 1; if (array.length > 4) { local = Integer.parseInt(array[4]); } + return local; + } - if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { - return createMap(name, config, local); + private CacheConfig copyConfig(CacheConfig source) { + CacheConfig target = new CacheConfig(); + target.setTTL(source.getTTL()); + target.setMaxIdleTime(source.getMaxIdleTime()); + target.setMaxSize(source.getMaxSize()); + target.setEvictionMode(source.getEvictionMode()); + for (MapEntryListener listener : source.getListeners()) { + target.addListener(listener); } - - return createMapCache(name, config, local); + return target; } /** @@ -177,17 +224,17 @@ public class PlusSpringCacheManager implements CacheManager { * @param local 是否启用本地一级缓存 * @return 缓存实例 */ - private Cache createMap(String name, CacheConfig config, int local) { + private Cache createMap(String cacheName, String name, CacheConfig config, int local) { RMap map = RedisUtils.getClient().getMap(name); Cache cache = new RedissonCache(map, allowNullValues); - if (local == 1) { - cache = new CaffeineCacheDecorator(name, cache); + if (local == 1 && caffeine != null) { + cache = new CaffeineCacheDecorator(cacheName, cache, caffeine); } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } - Cache oldCache = instanceMap.putIfAbsent(name, cache); + Cache oldCache = instanceMap.putIfAbsent(cacheName, cache); if (oldCache != null) { cache = oldCache; } @@ -202,17 +249,17 @@ public class PlusSpringCacheManager implements CacheManager { * @param local 是否启用本地一级缓存 * @return 缓存实例 */ - private Cache createMapCache(String name, CacheConfig config, int local) { + private Cache createMapCache(String cacheName, String name, CacheConfig config, int local) { RMapCache map = RedisUtils.getClient().getMapCache(name); Cache cache = new RedissonCache(map, config, allowNullValues); - if (local == 1) { - cache = new CaffeineCacheDecorator(name, cache); + if (local == 1 && caffeine != null) { + cache = new CaffeineCacheDecorator(cacheName, cache, caffeine); } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } - Cache oldCache = instanceMap.putIfAbsent(name, cache); + Cache oldCache = instanceMap.putIfAbsent(cacheName, cache); if (oldCache != null) { cache = oldCache; } else { diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java index 28e67d5df..6f7dfe140 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java @@ -15,8 +15,6 @@ import org.springframework.cache.CacheManager; @SuppressWarnings(value = {"unchecked"}) public class CacheUtils { - private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); - /** * 获取缓存值 * @@ -25,7 +23,7 @@ public class CacheUtils { * @return 缓存值 */ public static T get(String cacheNames, Object key) { - Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key); + Cache.ValueWrapper wrapper = getRequiredCache(cacheNames).get(key); return wrapper != null ? (T) wrapper.get() : null; } @@ -37,7 +35,7 @@ public class CacheUtils { * @param value 缓存值 */ public static void put(String cacheNames, Object key, Object value) { - CACHE_MANAGER.getCache(cacheNames).put(key, value); + getRequiredCache(cacheNames).put(key, value); } /** @@ -47,7 +45,7 @@ public class CacheUtils { * @param key 缓存key */ public static void evict(String cacheNames, Object key) { - CACHE_MANAGER.getCache(cacheNames).evict(key); + getRequiredCache(cacheNames).evict(key); } /** @@ -56,7 +54,19 @@ public class CacheUtils { * @param cacheNames 缓存组名称 */ public static void clear(String cacheNames) { - CACHE_MANAGER.getCache(cacheNames).clear(); + getRequiredCache(cacheNames).clear(); + } + + private static Cache getRequiredCache(String cacheNames) { + Cache cache = CacheManagerHolder.CACHE_MANAGER.getCache(cacheNames); + if (cache == null) { + throw new IllegalArgumentException("Cache '" + cacheNames + "' does not exist."); + } + return cache; + } + + private static class CacheManagerHolder { + private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); } } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java index b3fd462a2..abb4a9d0f 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java @@ -77,7 +77,9 @@ public class RedisUtils { public static void publish(String channelKey, T msg, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey); topic.publish(msg); - consumer.accept(msg); + if (consumer != null) { + consumer.accept(msg); + } } /** @@ -99,8 +101,31 @@ public class RedisUtils { * @param consumer 自定义处理 */ public static void subscribe(String channelKey, Class clazz, Consumer consumer) { + subscribeAndGetListenerId(channelKey, clazz, consumer); + } + + /** + * 订阅通道接收消息,并返回监听器 ID。 + * + * @param channelKey 通道key + * @param clazz 消息类型 + * @param consumer 自定义处理 + * @return 监听器 ID,可用于取消订阅 + */ + public static int subscribeAndGetListenerId(String channelKey, Class clazz, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey); - topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); + return topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); + } + + /** + * 取消通道订阅。 + * + * @param channelKey 通道key + * @param listenerId 监听器 ID + */ + public static void unsubscribe(String channelKey, int listenerId) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.removeListener(listenerId); } /** @@ -128,7 +153,7 @@ public class RedisUtils { bucket.setAndKeepTTL(value); } catch (Exception e) { long timeToLive = bucket.remainTimeToLive(); - if (timeToLive == -1) { + if (timeToLive <= 0) { bucket.set(value); } else { bucket.set(value, Duration.ofMillis(timeToLive)); @@ -239,6 +264,9 @@ public class RedisUtils { * @param key 缓存的键值 */ public static boolean deleteObject(final String key) { + if (key == null) { + return false; + } return CLIENT.getBucket(key).delete(); } @@ -247,7 +275,10 @@ public class RedisUtils { * * @param collection 多个对象 */ - public static void deleteObject(final Collection collection) { + public static void deleteObject(final Collection collection) { + if (collection == null || collection.isEmpty()) { + return; + } RBatch batch = CLIENT.createBatch(); collection.forEach(t -> { batch.getBucket(t.toString()).deleteAsync(); @@ -407,7 +438,7 @@ public class RedisUtils { */ public static Map getCacheMap(final String key) { RMap rMap = CLIENT.getMap(key); - return rMap.getAll(rMap.keySet()); + return rMap.readAllMap(); } /**