Переглянути джерело

[重大更新] 新增 PlusCacheWrapper装饰器 为SpringCache增加本地缓存减少redis查询提高性能(尝试性更新问题未知 请勿轻易更新尝试)

疯狂的狮子Li 1 рік тому
батько
коміт
57318cc55d

+ 5 - 0
ruoyi-common/ruoyi-common-redis/pom.xml

@@ -32,6 +32,11 @@
             <groupId>com.baomidou</groupId>
             <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 96 - 0
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusCacheWrapper.java

@@ -0,0 +1,96 @@
+package org.dromara.common.redis.manager;
+
+import cn.hutool.core.lang.Console;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.springframework.cache.Cache;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Cache 装饰器(用于扩展一级缓存)
+ *
+ * @author LionLi
+ */
+public class PlusCacheWrapper implements Cache {
+
+    private static final com.github.benmanes.caffeine.cache.Cache<Object, Object> CAFFEINE = Caffeine.newBuilder()
+        // 设置最后一次写入或访问后经过固定时间过期
+        .expireAfterWrite(30, TimeUnit.SECONDS)
+        // 初始的缓存空间大小
+        .initialCapacity(100)
+        // 缓存的最大条数
+        .maximumSize(1000)
+        .build();
+
+    private final Cache cache;
+
+    public PlusCacheWrapper(Cache cache) {
+        this.cache = cache;
+    }
+
+    @Override
+    public String getName() {
+        return cache.getName();
+    }
+
+    @Override
+    public Object getNativeCache() {
+        return cache.getNativeCache();
+    }
+
+    @Override
+    public ValueWrapper get(Object key) {
+        Object o = CAFFEINE.get(key, k -> cache.get(key));
+        Console.log("redisson caffeine -> key: " + key + ",value:" + o);
+        return (ValueWrapper) o;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T get(Object key, Class<T> type) {
+        Object o = CAFFEINE.get(key, k -> cache.get(key, type));
+        Console.log("redisson caffeine -> key: " + key + ",value:" + o);
+        return (T) o;
+    }
+
+    @Override
+    public void put(Object key, Object value) {
+        cache.put(key, value);
+        CAFFEINE.put(key, value);
+    }
+
+    public ValueWrapper putIfAbsent(Object key, Object value) {
+        return cache.putIfAbsent(key, value);
+    }
+
+    @Override
+    public void evict(Object key) {
+        evictIfPresent(key);
+    }
+
+    public boolean evictIfPresent(Object key) {
+        boolean b = cache.evictIfPresent(key);
+        if (b) {
+            CAFFEINE.invalidate(key);
+        }
+        return b;
+    }
+
+    @Override
+    public void clear() {
+        cache.clear();
+    }
+
+    public boolean invalidate() {
+        return cache.invalidate();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T get(Object key, Callable<T> valueLoader) {
+        Object o = CAFFEINE.get(key, k -> cache.get(key, valueLoader));
+        Console.log("redisson caffeine -> key: " + key + ",value:" + o);
+        return (T) o;
+    }
+
+}

+ 2 - 2
ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java

@@ -156,7 +156,7 @@ public class PlusSpringCacheManager implements CacheManager {
     private Cache createMap(String name, CacheConfig config) {
         RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
 
-        Cache cache = new RedissonCache(map, allowNullValues);
+        Cache cache = new PlusCacheWrapper(new RedissonCache(map, allowNullValues));
         if (transactionAware) {
             cache = new TransactionAwareCacheDecorator(cache);
         }
@@ -170,7 +170,7 @@ public class PlusSpringCacheManager implements CacheManager {
     private Cache createMapCache(String name, CacheConfig config) {
         RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
 
-        Cache cache = new RedissonCache(map, config, allowNullValues);
+        Cache cache = new PlusCacheWrapper(new RedissonCache(map, config, allowNullValues));
         if (transactionAware) {
             cache = new TransactionAwareCacheDecorator(cache);
         }

+ 3 - 1
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java

@@ -15,6 +15,8 @@ import java.util.concurrent.TimeUnit;
 
 /**
  * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
+ * <p>
+ * 采用 caffeine + redis 多级缓存 优化并发查询效率
  *
  * @author Lion Li
  */
@@ -22,7 +24,7 @@ public class PlusSaTokenDao implements SaTokenDao {
 
     private static final Cache<String, Object> CAFFEINE = Caffeine.newBuilder()
         // 设置最后一次写入或访问后经过固定时间过期
-        .expireAfterWrite(10, TimeUnit.SECONDS)
+        .expireAfterWrite(5, TimeUnit.SECONDS)
         // 初始的缓存空间大小
         .initialCapacity(100)
         // 缓存的最大条数

+ 1 - 11
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java

@@ -59,9 +59,6 @@ public class SecurityConfig implements WebMvcConfigurer {
                                 StpUtil.getTokenValue());
                         }
 
-                        // 保存用户信息
-//                        ThreadLocalHolder.set(LoginHelper.LOGIN_USER_KEY, LoginHelper.getLoginUser());
-
                         // 有效率影响 用于临时测试
                         // if (log.isDebugEnabled()) {
                         //     log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
@@ -69,14 +66,7 @@ public class SecurityConfig implements WebMvcConfigurer {
                         // }
 
                     });
-            })
-//            {
-//                @Override
-//                public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
-//                    ThreadLocalHolder.clear();
-//                }
-//            }
-            ).addPathPatterns("/**")
+            })).addPathPatterns("/**")
             // 排除不需要拦截的路径
             .excludePathPatterns(securityProperties.getExcludes());
     }

+ 3 - 19
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java

@@ -6,9 +6,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.dromara.common.core.constant.CacheConstants;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.CacheNames;
-import org.dromara.common.core.context.ThreadLocalHolder;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.service.DictService;
 import org.dromara.common.core.utils.MapstructUtils;
@@ -26,7 +25,6 @@ import org.dromara.system.domain.vo.SysDictTypeVo;
 import org.dromara.system.mapper.SysDictDataMapper;
 import org.dromara.system.mapper.SysDictTypeMapper;
 import org.dromara.system.service.ISysDictTypeService;
-import lombok.RequiredArgsConstructor;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
@@ -217,16 +215,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
      * @param separator 分隔符
      * @return 字典标签
      */
-    @SuppressWarnings("unchecked cast")
     @Override
     public String getDictLabel(String dictType, String dictValue, String separator) {
-        // 优先从本地缓存获取
-        List<SysDictDataVo> datas = ThreadLocalHolder.get(CacheConstants.SYS_DICT_KEY + dictType);
-        if (ObjectUtil.isNull(datas)) {
-            datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
-            ThreadLocalHolder.set(CacheConstants.SYS_DICT_KEY + dictType, datas);
-        }
-
+        List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
         Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
         if (StringUtils.containsAny(dictValue, separator)) {
             return Arrays.stream(dictValue.split(separator))
@@ -245,16 +236,9 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
      * @param separator 分隔符
      * @return 字典值
      */
-    @SuppressWarnings("unchecked cast")
     @Override
     public String getDictValue(String dictType, String dictLabel, String separator) {
-        // 优先从本地缓存获取
-        List<SysDictDataVo> datas = ThreadLocalHolder.get(CacheConstants.SYS_DICT_KEY + dictType);
-        if (ObjectUtil.isNull(datas)) {
-            datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
-            ThreadLocalHolder.set(CacheConstants.SYS_DICT_KEY + dictType, datas);
-        }
-
+        List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
         Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue);
         if (StringUtils.containsAny(dictLabel, separator)) {
             return Arrays.stream(dictLabel.split(separator))