From c9260c49660137967a8f44485979829b9b0be38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Mon, 7 Feb 2022 15:13:15 +0000 Subject: [PATCH 1/5] update README.md. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 353e087fc..896d6c3ac 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +# 此分支进入维护阶段 +> 不进行更新 只修复bug 持续维护到2022年10月
+> 推荐使用 4.X 版本
+> 主分支与Sa-Token分支合并 + ## 平台简介 [![码云Gitee](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![GitHub](https://img.shields.io/github/stars/JavaLionLi/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/JavaLionLi/RuoYi-Vue-Plus) From 82e8ab2385b8d0b5c4f11cbdcee42178896fe61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Fri, 18 Feb 2022 10:39:53 +0800 Subject: [PATCH 2/5] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 896d6c3ac..353e087fc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,3 @@ -# 此分支进入维护阶段 -> 不进行更新 只修复bug 持续维护到2022年10月
-> 推荐使用 4.X 版本
-> 主分支与Sa-Token分支合并 - ## 平台简介 [![码云Gitee](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus) [![GitHub](https://img.shields.io/github/stars/JavaLionLi/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/JavaLionLi/RuoYi-Vue-Plus) From f3a4104fd061a8895fa5c44e6d9260b718d6311a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90li?= <15040126243@163.com> Date: Fri, 18 Feb 2022 14:05:32 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20minio=20?= =?UTF-8?q?=E9=80=82=E9=85=8D=20https=20=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruoyi/oss/service/impl/MinioOssStrategy.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java b/ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java index eb2e77042..93f88fb3a 100644 --- a/ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java +++ b/ruoyi-oss/src/main/java/com/ruoyi/oss/service/impl/MinioOssStrategy.java @@ -9,6 +9,8 @@ import com.ruoyi.oss.exception.OssException; import com.ruoyi.oss.properties.OssProperties; import com.ruoyi.oss.service.abstractd.AbstractOssStrategy; import io.minio.*; +import io.minio.http.HttpUtils; +import okhttp3.HttpUrl; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -29,10 +31,15 @@ public class MinioOssStrategy extends AbstractOssStrategy { public void init(OssProperties ossProperties) { super.init(ossProperties); try { - minioClient = MinioClient.builder() - .endpoint(properties.getEndpoint(), 443, OssConstant.IS_HTTPS.equals(ossProperties.getIsHttps())) - .credentials(properties.getAccessKey(), properties.getSecretKey()) - .build(); + MinioClient.Builder builder = MinioClient.builder(); + if (OssConstant.IS_HTTPS.equals(ossProperties.getIsHttps())) { + HttpUrl url = HttpUtils.getBaseUrl(properties.getEndpoint()) + .newBuilder().scheme("https").build(); + builder.endpoint(url); + } else { + builder.endpoint(properties.getEndpoint()); + } + minioClient = builder.credentials(properties.getAccessKey(), properties.getSecretKey()).build(); createBucket(); } catch (Exception e) { throw new OssException("Minio存储配置错误! 请检查系统配置:[" + e.getMessage() + "]"); From 329c070e058b05e014c4f2d226098949e6df0a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 18 Feb 2022 16:20:58 +0000 Subject: [PATCH 4/5] =?UTF-8?q?update=20=E6=9B=B4=E6=96=B0=20=E5=A4=9A?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=A4=9A=E8=AE=BE=E5=A4=87=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ruoyi/common/helper/LoginHelper.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java index a15220859..cd0d25cfd 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/helper/LoginHelper.java @@ -12,7 +12,13 @@ import lombok.NoArgsConstructor; /** * 登录鉴权助手 - * 为适配多端登录而封装 + * + * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app + * deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios + * 可以组成 用户类型与设备类型多对多的 权限灵活控制 + * + * 多用户体系 针对 多种用户类型 但权限控制不一致 + * 可以组成 多用户类型表与多设备类型 分别控制权限 * * @author Lion Li */ @@ -26,7 +32,6 @@ public class LoginHelper { /** * 登录系统 - * 针对两套用户体系 * * @param loginUser 登录用户信息 */ @@ -38,7 +43,7 @@ public class LoginHelper { /** * 登录系统 基于 设备类型 - * 针对一套用户体系 + * 针对相同用户体系不同设备 * * @param loginUser 登录用户信息 */ From 654180e677e1faa475112c7147b2f19f3dd8be25 Mon Sep 17 00:00:00 2001 From: yongen Date: Tue, 22 Mar 2022 16:33:52 +0800 Subject: [PATCH 5/5] =?UTF-8?q?1:=20=E5=A2=9E=E5=8A=A0redis=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=88=9D=E5=A7=8B=E5=8C=96=E6=8C=87=E5=AE=9Akey?= =?UTF-8?q?=E5=89=8D=E7=BC=80=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=A4=9A=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=85=B1=E7=94=A8=E4=B8=80=E4=B8=AAredis=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E6=97=B6key=E8=A6=86=E7=9B=96=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82=202:=20=E5=A2=9E=E5=8A=A0redis=E5=93=A8?= =?UTF-8?q?=E5=85=B5=E9=85=8D=E7=BD=AE=E6=A8=A1=E5=BC=8F=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=87=AA=E5=8A=A8=E9=85=8D=E7=BD=AE=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-dev.yml | 39 ++- .../ruoyi/framework/config/RedisConfig.java | 179 ----------- .../config/properties/RedissonProperties.java | 40 +++ .../config/redisson/KeyPrefixMapper.java | 42 +++ .../config/redisson/RedisConfig.java | 293 ++++++++++++++++++ 5 files changed, 411 insertions(+), 182 deletions(-) delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/KeyPrefixMapper.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/RedisConfig.java diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index ba38be663..f881d8816 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -19,7 +19,7 @@ xxl: # 执行器开关 enabled: true # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。 - admin-addresses: http://localhost:9100/xxl-job-admin + admin-addresses: http://127.0.0.1:9870/xxl-job-admin/ # 执行器通讯TOKEN:非空时启用 access-token: xxl-job # 执行器配置 @@ -55,7 +55,7 @@ spring: # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root - password: root + password: 12345678 # 从库数据源 slave: lazy: true @@ -121,7 +121,7 @@ spring: # 数据库索引 database: 0 # 密码 - password: + password: 12345678 # 连接超时时间 timeout: 10s # 是否开启ssl @@ -134,6 +134,10 @@ redisson: nettyThreads: 8 # 传输模式 transportMode: "NIO" + # 缓存前缀 + key-prefix: ${ruoyi.name} + # 配置方式 + mode: "SINGLE" # 单节点配置 singleServerConfig: # 客户端名称 @@ -152,3 +156,32 @@ redisson: retryInterval: 1500 # 发布和订阅连接池大小 subscriptionConnectionPoolSize: 50 + # 集群配置 +# clusterServersConfig: +# # 客户端名称 +# clientName: ${ruoyi.name} +# # master最小空闲连接数 +# masterConnectionMinimumIdleSize: 32 +# # master连接池大小 +# masterConnectionPoolSize: 64 +# # slave最小空闲连接数 +# slaveConnectionMinimumIdleSize: 32 +# # slave连接池大小 +# slaveConnectionPoolSize: 64 +# # 连接空闲超时,单位:毫秒 +# idleConnectionTimeout: 10000 +# # ping连接间隔 +# pingConnectionInterval: 1000 +# # 命令等待超时,单位:毫秒 +# timeout: 3000 +# # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 +# retryAttempts: 3 +# # 命令重试发送时间间隔,单位:毫秒 +# retryInterval: 1500 +# # 发布和订阅连接池大小 +# subscriptionConnectionPoolSize: 50 +# # 读取模式 +# readMode: "SLAVE" +# # 订阅模式 +# subscriptionMode: "MASTER" + diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java deleted file mode 100644 index a8bec417a..000000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.ruoyi.framework.config; - -import cn.hutool.core.util.ObjectUtil; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.config.properties.RedissonProperties; -import lombok.extern.slf4j.Slf4j; -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.codec.JsonJacksonCodec; -import org.redisson.config.Config; -import org.redisson.spring.cache.CacheConfig; -import org.redisson.spring.cache.RedissonSpringCacheManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.CachingConfigurerSupport; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * redis配置 - * - * @author Lion Li - */ -@Slf4j -@Configuration -@EnableCaching -public class RedisConfig extends CachingConfigurerSupport { - - private static final String REDIS_PROTOCOL_PREFIX = "redis://"; - private static final String REDISS_PROTOCOL_PREFIX = "rediss://"; - - @Autowired - private RedisProperties redisProperties; - - @Autowired - private RedissonProperties redissonProperties; - - @Primary - @Bean(destroyMethod = "shutdown") - public RedissonClient redisson() { - String prefix = REDIS_PROTOCOL_PREFIX; - if (redisProperties.isSsl()) { - prefix = REDISS_PROTOCOL_PREFIX; - } - Config config = new Config(); - config.setThreads(redissonProperties.getThreads()) - .setNettyThreads(redissonProperties.getNettyThreads()) - .setCodec(JsonJacksonCodec.INSTANCE) - .setTransportMode(redissonProperties.getTransportMode()); - - RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); - if (ObjectUtil.isNotNull(singleServerConfig)) { - // 使用单机模式 - config.useSingleServer() - .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort()) - .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) - .setDatabase(redisProperties.getDatabase()) - .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) - .setTimeout(singleServerConfig.getTimeout()) - .setRetryAttempts(singleServerConfig.getRetryAttempts()) - .setRetryInterval(singleServerConfig.getRetryInterval()) - .setClientName(singleServerConfig.getClientName()) - .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) - .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) - .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) - .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()); - } - // 集群配置方式 参考下方注释 - RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); - if (ObjectUtil.isNotNull(clusterServersConfig)) { - // 使用集群模式 - String finalPrefix = prefix; - List nodes = redisProperties.getCluster().getNodes() - .stream() - .map(node -> finalPrefix + node) - .collect(Collectors.toList()); - - config.useClusterServers() - .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) - .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) - .setTimeout(clusterServersConfig.getTimeout()) - .setRetryAttempts(clusterServersConfig.getRetryAttempts()) - .setRetryInterval(clusterServersConfig.getRetryInterval()) - .setClientName(clusterServersConfig.getClientName()) - .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) - .setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval()) - .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) - .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) - .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) - .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) - .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) - .setReadMode(clusterServersConfig.getReadMode()) - .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()) - .setNodeAddresses(nodes); - } - RedissonClient redissonClient = Redisson.create(config); - log.info("初始化 redis 配置"); - return redissonClient; - } - - /** - * 整合spring-cache - */ - @Bean - public CacheManager cacheManager(RedissonClient redissonClient) { - List cacheGroup = redissonProperties.getCacheGroup(); - Map config = new HashMap<>(); - for (RedissonProperties.CacheGroup group : cacheGroup) { - CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime()); - cacheConfig.setMaxSize(group.getMaxSize()); - config.put(group.getGroupId(), cacheConfig); - } - return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE); - } - - /** - * redis集群配置 yml - * - * --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉) - * spring: - * redis: - * cluster: - * nodes: - * - 192.168.0.100:6379 - * - 192.168.0.101:6379 - * - 192.168.0.102:6379 - * # 密码 - * password: - * # 连接超时时间 - * timeout: 10s - * # 是否开启ssl - * ssl: false - * - * redisson: - * # 线程池数量 - * threads: 16 - * # Netty线程池数量 - * nettyThreads: 32 - * # 传输模式 - * transportMode: "NIO" - * # 集群配置 - * clusterServersConfig: - * # 客户端名称 - * clientName: ${ruoyi.name} - * # master最小空闲连接数 - * masterConnectionMinimumIdleSize: 32 - * # master连接池大小 - * masterConnectionPoolSize: 64 - * # slave最小空闲连接数 - * slaveConnectionMinimumIdleSize: 32 - * # slave连接池大小 - * slaveConnectionPoolSize: 64 - * # 连接空闲超时,单位:毫秒 - * idleConnectionTimeout: 10000 - * # ping连接间隔 - * pingConnectionInterval: 1000 - * # 命令等待超时,单位:毫秒 - * timeout: 3000 - * # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 - * retryAttempts: 3 - * # 命令重试发送时间间隔,单位:毫秒 - * retryInterval: 1500 - * # 发布和订阅连接池大小 - * subscriptionConnectionPoolSize: 50 - * # 读取模式 - * readMode: "SLAVE" - * # 订阅模式 - * subscriptionMode: "MASTER" - */ - -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java index eae9e3fc8..b06ac2a21 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java @@ -1,6 +1,10 @@ package com.ruoyi.framework.config.properties; +import com.ruoyi.common.core.domain.R; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; import org.redisson.config.ReadMode; import org.redisson.config.SubscriptionMode; @@ -20,6 +24,15 @@ import java.util.List; @ConfigurationProperties(prefix = "redisson") public class RedissonProperties { + + /** + * 连接模式 + */ + public static final String MODE_SINGLE = "SINGLE"; + public static final String MODE_SENTINEL = "SENTINEL"; + public static final String MODE_CLUSTER = "CLUSTER"; + + /** * 线程池数量,默认值 = 当前处理核数量 * 2 */ @@ -50,6 +63,16 @@ public class RedissonProperties { */ private List cacheGroup; + /** + * 缓存key前缀 + */ + private String keyPrefix; + + /** + * 配置方式 + */ + private RedisMode mode; + @Data @NoArgsConstructor public static class SingleServerConfig { @@ -193,4 +216,21 @@ public class RedissonProperties { } + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public enum RedisMode { + /** + * 单机 + */ + SINGLE, + /** + * 集群 + */ + CLUSTER, + /** + * 哨兵 + */ + SENTINEL; + + } + } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/KeyPrefixMapper.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/KeyPrefixMapper.java new file mode 100644 index 000000000..ffe5579b8 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/KeyPrefixMapper.java @@ -0,0 +1,42 @@ +package com.ruoyi.framework.config.redisson; + +import org.apache.commons.lang3.StringUtils; +import org.redisson.api.NameMapper; + +/** + * redisson key 自动加前缀 解决多应用共用一个redis环境时key覆盖的问题 + * + * @author im_libin@yeah.net + * @date 2022/3/21 2:50 下午 + */ +public class KeyPrefixMapper implements NameMapper { + + private final String keyPrefix; + + public KeyPrefixMapper(String keyPrefix) { + this.keyPrefix = keyPrefix + ":"; + } + + @Override + public String map(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (!name.startsWith(keyPrefix)) { + return keyPrefix + name; + } else { + return name; + } + } + + @Override + public String unmap(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + if (name.startsWith(keyPrefix)) { + return name.substring(keyPrefix.length()); + } + return name; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/RedisConfig.java new file mode 100644 index 000000000..fa2a9a32d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/redisson/RedisConfig.java @@ -0,0 +1,293 @@ +package com.ruoyi.framework.config.redisson; + +import cn.hutool.core.util.ObjectUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.config.properties.RedissonProperties; +import com.ruoyi.framework.config.properties.RedissonProperties.ClusterServersConfig; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.redisson.Redisson; +import org.redisson.api.DefaultNameMapper; +import org.redisson.api.NameMapper; +import org.redisson.api.RedissonClient; +import org.redisson.codec.JsonJacksonCodec; +import org.redisson.config.Config; +import org.redisson.spring.cache.CacheConfig; +import org.redisson.spring.cache.RedissonSpringCacheManager; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +/** + * redis配置 + * + * @author Lion Li + */ +@Slf4j +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + private static final String REDIS_PROTOCOL_PREFIX = "redis://"; + private static final String REDISS_PROTOCOL_PREFIX = "rediss://"; + + @Resource + private RedisProperties redisProperties; + + @Resource + private RedissonProperties redissonProperties; + + /** + * 单节点模式 + */ + @Primary + @Bean(name = "redissonClient", destroyMethod = "shutdown") + @ConditionalOnProperty(name = "redisson.mode", havingValue = RedissonProperties.MODE_SINGLE) + @ConditionalOnMissingBean(RedissonClient.class) + RedissonClient single() { + RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); + if (ObjectUtil.isNull(singleServerConfig)) { + throw new IllegalArgumentException("Redis connection 'redisson.singleServerConfig' is empty"); + } + Config config = getConfig(); + // 单机模式 + config.useSingleServer() + .setNameMapper(getNameMapper()) + .setAddress(getRedisProtocolPrefix() + redisProperties.getHost() + ":" + redisProperties.getPort()) + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setDatabase(redisProperties.getDatabase()) + .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setTimeout(singleServerConfig.getTimeout()) + .setRetryAttempts(singleServerConfig.getRetryAttempts()) + .setRetryInterval(singleServerConfig.getRetryInterval()) + .setClientName(singleServerConfig.getClientName()) + .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) + .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) + .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()); + log.info("初始化 redis 配置: [mode={}, address={}]", redissonProperties.getMode(), redisProperties.getHost() + ":" + redisProperties.getPort()); + return Redisson.create(config); + } + + /** + * 哨兵模式 + */ + @Primary + @Bean(name = "redissonClient", destroyMethod = "shutdown") + @ConditionalOnProperty(name = "redisson.mode", havingValue = RedissonProperties.MODE_SENTINEL) + @ConditionalOnMissingBean(RedissonClient.class) + RedissonClient sentinel() { + RedissonProperties.ClusterServersConfig clusterServersConfig = getClusterServersConfig(); + String finalPrefix = getRedisProtocolPrefix(); + List nodes = redisProperties.getSentinel().getNodes() + .stream() + .map(node -> finalPrefix + node) + .collect(Collectors.toList()); + Config config = getConfig(); + // 哨兵方式 + config.useSentinelServers() + .setCheckSentinelsList(false) + .setNameMapper(getNameMapper()) + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setMasterName(redisProperties.getSentinel().getMaster()) + .setTimeout(clusterServersConfig.getTimeout()) + .setRetryAttempts(clusterServersConfig.getRetryAttempts()) + .setRetryInterval(clusterServersConfig.getRetryInterval()) + .setClientName(clusterServersConfig.getClientName()) + .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) + .setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval()) + .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) + .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) + .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) + .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) + .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) + .setReadMode(clusterServersConfig.getReadMode()) + .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()) + .setSentinelAddresses(nodes); + log.info("初始化 redis 配置: [mode={}, master={}, nodes={}]", redissonProperties.getMode(), redisProperties.getSentinel().getMaster(), + nodes); + return Redisson.create(config); + } + + + /** + * 集群模式 + */ + @Primary + @Bean(name = "redissonClient", destroyMethod = "shutdown") + @ConditionalOnProperty(name = "redisson.mode", havingValue = RedissonProperties.MODE_CLUSTER) + @ConditionalOnMissingBean(RedissonClient.class) + RedissonClient cluster() { + RedissonProperties.ClusterServersConfig clusterServersConfig = getClusterServersConfig(); + String finalPrefix = getRedisProtocolPrefix(); + List nodes = redisProperties.getCluster().getNodes() + .stream() + .map(node -> finalPrefix + node) + .collect(Collectors.toList()); + Config config = getConfig(); + // 集群方式 + config.useClusterServers() + .setNameMapper(getNameMapper()) + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setTimeout(clusterServersConfig.getTimeout()) + .setRetryAttempts(clusterServersConfig.getRetryAttempts()) + .setRetryInterval(clusterServersConfig.getRetryInterval()) + .setClientName(clusterServersConfig.getClientName()) + .setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout()) + .setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval()) + .setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize()) + .setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize()) + .setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize()) + .setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize()) + .setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize()) + .setReadMode(clusterServersConfig.getReadMode()) + .setSubscriptionMode(clusterServersConfig.getSubscriptionMode()) + .setNodeAddresses(nodes); + log.info("初始化 redis 配置: [mode={}, nodes={}]", redissonProperties.getMode(), nodes); + return Redisson.create(config); + } + + private String getRedisProtocolPrefix(){ + return redisProperties.isSsl() ? REDISS_PROTOCOL_PREFIX : REDIS_PROTOCOL_PREFIX; + } + + private Config getConfig(){ + Config config = new Config(); + config.setThreads(redissonProperties.getThreads()) + .setNettyThreads(redissonProperties.getNettyThreads()) + .setCodec(JsonJacksonCodec.INSTANCE) + .setTransportMode(redissonProperties.getTransportMode()); + return config; + } + + private ClusterServersConfig getClusterServersConfig() { + RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig(); + if (ObjectUtil.isNull(clusterServersConfig)){ + throw new IllegalArgumentException("Redis connection 'redisson.clusterServersConfig' is empty"); + } + return clusterServersConfig; + } + + private NameMapper getNameMapper() { + if (StringUtils.isBlank(redissonProperties.getKeyPrefix())){ + return new DefaultNameMapper(); + } + return new KeyPrefixMapper(redissonProperties.getKeyPrefix()); + } + + + /** + * 整合spring-cache + */ + @Bean + public CacheManager cacheManager(RedissonClient redissonClient) { + List cacheGroup = redissonProperties.getCacheGroup(); + Map config = new HashMap<>(cacheGroup.size()); + for (RedissonProperties.CacheGroup group : cacheGroup) { + CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime()); + cacheConfig.setMaxSize(group.getMaxSize()); + config.put(group.getGroupId(), cacheConfig); + } + return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE); + } + + /** + * redis集群配置 yml + * + * --- # redis 集群配置(单机、集群、哨兵只能开启一个另两个需要注释掉) + * spring: + * redis: + * sentinel: + * master: redis_master + * nodes: + * - 192.168.0.100:26379 + * - 192.168.0.101:26379 + * - 192.168.0.102:26379 + * cluster: + * nodes: + * - 192.168.0.100:6379 + * - 192.168.0.101:6379 + * - 192.168.0.102:6379 + * # 地址 + * host: localhost + * # 端口,默认为6379 + * port: 6379 + * # 密码 + * password: 12345678 + * # 连接超时时间 + * timeout: 10s + * # 是否开启ssl + * ssl: false + * + * redisson: + * # 线程池数量 + * threads: 16 + * # Netty线程池数量 + * nettyThreads: 32 + * # 传输模式 + * transportMode: "NIO" + * # 缓存key前缀 + * keyPrefix: ${ruoyi.name} + * # 配置方式 + * mode: "SINGLE" + * --- # 当mode: "SINGLE" 配置 singleServerConfig节点,当mode: "CLUSTER/SENTINEL"时 配置clusterServersConfig节点 + * # 单节点配置 + * singleServerConfig: + * # 客户端名称 + * clientName: ${ruoyi.name} + * # 最小空闲连接数 + * connectionMinimumIdleSize: 8 + * # 连接池大小 + * connectionPoolSize: 32 + * # 连接空闲超时,单位:毫秒 + * idleConnectionTimeout: 10000 + * # 命令等待超时,单位:毫秒 + * timeout: 3000 + * # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + * retryAttempts: 3 + * # 命令重试发送时间间隔,单位:毫秒 + * retryInterval: 1500 + * # 发布和订阅连接池大小 + * subscriptionConnectionPoolSize: 50 + * # 集群配置 + * clusterServersConfig: + * # 客户端名称 + * clientName: ${ruoyi.name} + * # master最小空闲连接数 + * masterConnectionMinimumIdleSize: 32 + * # master连接池大小 + * masterConnectionPoolSize: 64 + * # slave最小空闲连接数 + * slaveConnectionMinimumIdleSize: 32 + * # slave连接池大小 + * slaveConnectionPoolSize: 64 + * # 连接空闲超时,单位:毫秒 + * idleConnectionTimeout: 10000 + * # ping连接间隔 + * pingConnectionInterval: 1000 + * # 命令等待超时,单位:毫秒 + * timeout: 3000 + * # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + * retryAttempts: 3 + * # 命令重试发送时间间隔,单位:毫秒 + * retryInterval: 1500 + * # 发布和订阅连接池大小 + * subscriptionConnectionPoolSize: 50 + * # 读取模式 + * readMode: "SLAVE" + * # 订阅模式 + * subscriptionMode: "MASTER" + */ +}