ソースを参照

[重大更新] 使用caffeine重构PlusSaTokenDao层实现 减少将近90%的redis查询提高性能(尝试性更新问题未知 请勿轻易更新尝试)

疯狂的狮子Li 2 年 前
コミット
25012e518e

+ 8 - 14
ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java

@@ -3,7 +3,6 @@ package org.dromara.auth.listener;
 import cn.dev33.satoken.config.SaTokenConfig;
 import cn.dev33.satoken.listener.SaTokenListener;
 import cn.dev33.satoken.stp.SaLoginModel;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.useragent.UserAgent;
 import cn.hutool.http.useragent.UserAgentUtil;
 import lombok.RequiredArgsConstructor;
@@ -21,11 +20,9 @@ import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.resource.api.RemoteMessageService;
 import org.dromara.system.api.RemoteUserService;
 import org.dromara.system.api.domain.SysUserOnline;
-import org.dromara.system.api.model.LoginUser;
 import org.springframework.stereotype.Component;
 
 import java.time.Duration;
-import java.util.concurrent.ScheduledExecutorService;
 
 /**
  * 用户行为 侦听器的实现
@@ -38,7 +35,6 @@ import java.util.concurrent.ScheduledExecutorService;
 public class UserActionListener implements SaTokenListener {
 
     private final SaTokenConfig tokenConfig;
-    private final ScheduledExecutorService scheduledExecutorService;
     @DubboReference
     private RemoteUserService remoteUserService;
     @DubboReference
@@ -51,7 +47,6 @@ public class UserActionListener implements SaTokenListener {
     public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
         UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
         String ip = ServletUtils.getClientIP();
-        LoginUser user = LoginHelper.getLoginUser();
         SysUserOnline userOnline = new SysUserOnline();
         userOnline.setIpaddr(ip);
         userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
@@ -59,12 +54,11 @@ public class UserActionListener implements SaTokenListener {
         userOnline.setOs(userAgent.getOs().getName());
         userOnline.setLoginTime(System.currentTimeMillis());
         userOnline.setTokenId(tokenValue);
-        userOnline.setUserName(user.getUsername());
-        userOnline.setClientKey(user.getClientKey());
-        userOnline.setDeviceType(user.getDeviceType());
-        if (ObjectUtil.isNotNull(user.getDeptName())) {
-            userOnline.setDeptName(user.getDeptName());
-        }
+        String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
+        userOnline.setUserName(username);
+        userOnline.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
+        userOnline.setDeviceType(loginModel.getDevice());
+        userOnline.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
         if (tokenConfig.getTimeout() == -1) {
             RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
         } else {
@@ -72,14 +66,14 @@ public class UserActionListener implements SaTokenListener {
         }
         // 记录登录日志
         LogininforEvent logininforEvent = new LogininforEvent();
-        logininforEvent.setTenantId(user.getTenantId());
-        logininforEvent.setUsername(user.getUsername());
+        logininforEvent.setTenantId((String) loginModel.getExtra(LoginHelper.TENANT_KEY));
+        logininforEvent.setUsername(username);
         logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
         logininforEvent.setMessage(MessageUtils.message("user.login.success"));
         logininforEvent.setRequest(ServletUtils.getRequest());
         SpringUtils.context().publishEvent(logininforEvent);
         // 更新登录信息
-        remoteUserService.recordLoginInfo(user.getUserId(), ServletUtils.getClientIP());
+        remoteUserService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
         log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
     }
 

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

@@ -52,6 +52,11 @@
             <artifactId>ruoyi-common-redis</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 30 - 5
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java

@@ -2,12 +2,16 @@ package org.dromara.common.satoken.core.dao;
 
 import cn.dev33.satoken.dao.SaTokenDao;
 import cn.dev33.satoken.util.SaFoxUtil;
+import cn.hutool.core.lang.Console;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
 import org.dromara.common.redis.utils.RedisUtils;
 
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Sa-Token持久层接口(使用框架自带RedisUtils实现 协议统一)
@@ -16,12 +20,23 @@ import java.util.List;
  */
 public class PlusSaTokenDao implements SaTokenDao {
 
+    private static final Cache<String, Object> CAFFEINE = Caffeine.newBuilder()
+        // 设置最后一次写入或访问后经过固定时间过期
+        .expireAfterWrite(10, TimeUnit.SECONDS)
+        // 初始的缓存空间大小
+        .initialCapacity(100)
+        // 缓存的最大条数
+        .maximumSize(1000)
+        .build();
+
     /**
      * 获取Value,如无返空
      */
     @Override
     public String get(String key) {
-        return RedisUtils.getCacheObject(key);
+        Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
+        Console.log("caffeine -> key:" + key + ",value:" + o);
+        return (String) o;
     }
 
     /**
@@ -38,6 +53,7 @@ public class PlusSaTokenDao implements SaTokenDao {
         } else {
             RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
         }
+        CAFFEINE.put(key, value);
     }
 
     /**
@@ -47,6 +63,7 @@ public class PlusSaTokenDao implements SaTokenDao {
     public void update(String key, String value) {
         if (RedisUtils.hasKey(key)) {
             RedisUtils.setCacheObject(key, value, true);
+            CAFFEINE.put(key, value);
         }
     }
 
@@ -81,7 +98,9 @@ public class PlusSaTokenDao implements SaTokenDao {
      */
     @Override
     public Object getObject(String key) {
-        return RedisUtils.getCacheObject(key);
+        Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
+        Console.log("caffeine -> key:" + key + ",value:" + o);
+        return o;
     }
 
     /**
@@ -98,6 +117,7 @@ public class PlusSaTokenDao implements SaTokenDao {
         } else {
             RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
         }
+        CAFFEINE.put(key, object);
     }
 
     /**
@@ -107,6 +127,7 @@ public class PlusSaTokenDao implements SaTokenDao {
     public void updateObject(String key, Object object) {
         if (RedisUtils.hasKey(key)) {
             RedisUtils.setCacheObject(key, object, true);
+            CAFFEINE.put(key, object);
         }
     }
 
@@ -139,10 +160,14 @@ public class PlusSaTokenDao implements SaTokenDao {
     /**
      * 搜索数据
      */
+    @SuppressWarnings("unchecked")
     @Override
     public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
-        Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
-        List<String> list = new ArrayList<>(keys);
-        return SaFoxUtil.searchList(list, start, size, sortType);
+        String keyStr = prefix + "*" + keyword + "*";
+        return (List<String>) CAFFEINE.get(keyStr, k -> {
+            Collection<String> keys = RedisUtils.keys(keyStr);
+            List<String> list = new ArrayList<>(keys);
+            return SaFoxUtil.searchList(list, start, size, sortType);
+        });
     }
 }

+ 22 - 35
ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java

@@ -9,12 +9,10 @@ import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import org.dromara.common.core.constant.TenantConstants;
 import org.dromara.common.core.constant.UserConstants;
-import org.dromara.common.core.context.ThreadLocalHolder;
 import org.dromara.common.core.enums.UserType;
 import org.dromara.system.api.model.LoginUser;
 
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * 登录鉴权助手
@@ -34,9 +32,10 @@ public class LoginHelper {
     public static final String LOGIN_USER_KEY = "loginUser";
     public static final String TENANT_KEY = "tenantId";
     public static final String USER_KEY = "userId";
+    public static final String USER_NAME_KEY = "userName";
     public static final String DEPT_KEY = "deptId";
+    public static final String DEPT_NAME_KEY = "deptName";
     public static final String CLIENT_KEY = "clientid";
-    public static final String TENANT_ADMIN_KEY = "isTenantAdmin";
 
     /**
      * 登录系统 基于 设备类型
@@ -46,15 +45,15 @@ public class LoginHelper {
      * @param model     配置参数
      */
     public static void login(LoginUser loginUser, SaLoginModel model) {
-        ThreadLocalHolder.set(LOGIN_USER_KEY, loginUser);
-        ThreadLocalHolder.set(TENANT_KEY, loginUser.getTenantId());
-        ThreadLocalHolder.set(USER_KEY, loginUser.getUserId());
-        ThreadLocalHolder.set(DEPT_KEY, loginUser.getDeptId());
         model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
         StpUtil.login(loginUser.getLoginId(),
             model.setExtra(TENANT_KEY, loginUser.getTenantId())
                 .setExtra(USER_KEY, loginUser.getUserId())
-                .setExtra(DEPT_KEY, loginUser.getDeptId()));
+                .setExtra(DEPT_KEY, loginUser.getDeptId())
+                .setExtra(TENANT_KEY, loginUser.getTenantId())
+                .setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
+                .setExtra(USER_NAME_KEY, loginUser.getUsername())
+        );
         SaSession tokenSession = StpUtil.getTokenSession();
         tokenSession.updateTimeout(model.getTimeout());
         tokenSession.set(LOGIN_USER_KEY, loginUser);
@@ -64,13 +63,11 @@ public class LoginHelper {
      * 获取用户(多级缓存)
      */
     public static LoginUser getLoginUser() {
-        return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> {
-            SaSession session = StpUtil.getTokenSession();
-            if (ObjectUtil.isNull(session)) {
-                return null;
-            }
-            return session.get(LOGIN_USER_KEY);
-        });
+        SaSession session = StpUtil.getTokenSession();
+        if (ObjectUtil.isNull(session)) {
+            return null;
+        }
+        return (LoginUser) session.get(LOGIN_USER_KEY);
     }
 
     /**
@@ -88,7 +85,7 @@ public class LoginHelper {
      * 获取用户id
      */
     public static Long getUserId() {
-        return  Convert.toLong(getExtra(USER_KEY));
+        return Convert.toLong(getExtra(USER_KEY));
     }
 
     /**
@@ -106,7 +103,12 @@ public class LoginHelper {
     }
 
     private static Object getExtra(String key) {
-        return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
+        try {
+            return StpUtil.getExtra(key);
+        } catch (Exception e) {
+            return null;
+        }
+
     }
 
     /**
@@ -120,8 +122,8 @@ public class LoginHelper {
      * 获取用户类型
      */
     public static UserType getUserType() {
-        String loginId = StpUtil.getLoginIdAsString();
-        return UserType.getUserType(loginId);
+        String loginType = StpUtil.getLoginIdAsString();
+        return UserType.getUserType(loginType);
     }
 
     /**
@@ -149,26 +151,11 @@ public class LoginHelper {
     }
 
     public static boolean isTenantAdmin() {
-        Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> {
-            return isTenantAdmin(getLoginUser().getRolePermission());
-        });
-        return Convert.toBool(value);
+        return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
     }
 
     public static boolean isLogin() {
         return getLoginUser() != null;
     }
 
-    public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
-        try {
-            Object obj = ThreadLocalHolder.get(key);
-            if (ObjectUtil.isNull(obj)) {
-                obj = handle.get();
-                ThreadLocalHolder.set(key, obj);
-            }
-            return obj;
-        } catch (Exception e) {
-            return null;
-        }
-    }
 }

+ 1 - 9
ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfiguration.java

@@ -5,10 +5,7 @@ import cn.dev33.satoken.filter.SaServletFilter;
 import cn.dev33.satoken.interceptor.SaInterceptor;
 import cn.dev33.satoken.same.SaSameUtil;
 import cn.dev33.satoken.util.SaResult;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
 import org.dromara.common.core.constant.HttpStatus;
-import org.dromara.common.core.context.ThreadLocalHolder;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@@ -28,12 +25,7 @@ public class SecurityConfiguration implements WebMvcConfigurer {
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
         // 注册路由拦截器,自定义验证规则
-        registry.addInterceptor(new SaInterceptor() {
-            @Override
-            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
-                ThreadLocalHolder.clear();
-            }
-        }).addPathPatterns("/**");
+        registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
     }
 
     /**