From 71a58178eb87bdb66a09296c91d9d7ec026609fa Mon Sep 17 00:00:00 2001 From: Wenchao Gong Date: Mon, 13 Sep 2021 14:33:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E5=85=B3=E4=BD=BF=E7=94=A8Redis?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E8=B7=AF=E7=94=B1=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-extend/ruoyi-isc-gateway/pom.xml | 25 ++-- .../ruoyi/gateway/config/GatewayConfig.java | 29 ++-- .../com/ruoyi/gateway/config/RedisConfig.java | 104 ++++++++++++++ .../ComplexRouteDefinitionRepository.java | 65 --------- .../RedisRouteDefinitionRepository.java | 24 ++-- .../config/provider/RedissonProperties.java | 134 ++++++++++++++++++ .../src/main/resources/application-dev.yml | 34 ++++- .../src/main/resources/application-local.yml | 76 ++++++++++ .../src/main/resources/application.yml | 12 ++ 9 files changed, 391 insertions(+), 112 deletions(-) create mode 100644 ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/RedisConfig.java delete mode 100644 ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/ComplexRouteDefinitionRepository.java create mode 100644 ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedissonProperties.java create mode 100644 ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-local.yml diff --git a/ruoyi-extend/ruoyi-isc-gateway/pom.xml b/ruoyi-extend/ruoyi-isc-gateway/pom.xml index d6fc777cf..4df6d0639 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/pom.xml +++ b/ruoyi-extend/ruoyi-isc-gateway/pom.xml @@ -20,26 +20,23 @@ org.springframework.boot spring-boot-starter-data-redis - - - io.lettuce - lettuce-core - - - redis.clients - jedis + org.redisson + redisson-spring-boot-starter - - de.codecentric spring-boot-admin-starter-client + + org.projectlombok + lombok + + + cn.hutool + hutool-all + - \ No newline at end of file + diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java index cc0eec028..678d3cb35 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java @@ -1,8 +1,7 @@ package com.ruoyi.gateway.config; -import com.ruoyi.gateway.config.provider.ComplexRouteDefinitionRepository; import com.ruoyi.gateway.config.provider.RedisRouteDefinitionRepository; -import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository; +import org.redisson.api.RedissonClient; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionRepository; import org.springframework.context.annotation.Bean; @@ -21,10 +20,13 @@ import javax.annotation.Resource; * @date 2021-09-11 */ @Configuration -public class GatewayConfig { +public class GatewayConfig +{ @Resource private RedisTemplate redisTemplate; + @Resource + private RedissonClient redissonClient; @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) @@ -40,29 +42,14 @@ public class GatewayConfig { return template; } - @Bean - public RouteDefinitionRepository complexRouteDefinitionRepository() - { - return new ComplexRouteDefinitionRepository(inMemoryRouteDefinitionRepository(), redisRouteDefinitionRepository()); - } - - /** - * 内存 路由仓库 - * - * @return 本地内存路由仓库 - */ - RouteDefinitionRepository inMemoryRouteDefinitionRepository() - { - return new InMemoryRouteDefinitionRepository(); - } - /** * Redis 路由仓库 * * @return redis路由仓库 */ - RouteDefinitionRepository redisRouteDefinitionRepository() + @Bean + public RouteDefinitionRepository redisRouteDefinitionRepository() { - return new RedisRouteDefinitionRepository(redisTemplate); + return new RedisRouteDefinitionRepository(redissonClient); } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/RedisConfig.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/RedisConfig.java new file mode 100644 index 000000000..adc4f28da --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/RedisConfig.java @@ -0,0 +1,104 @@ +package com.ruoyi.gateway.config; + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.gateway.config.provider.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.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +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.data.redis.core.script.DefaultRedisScript; + +import java.io.IOException; + +/** + * 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; + + @Bean(destroyMethod = "shutdown") + @ConditionalOnMissingBean(RedissonClient.class) + public RedissonClient redisson() throws IOException { + 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(); + // 使用单机模式 + config.useSingleServer() + .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort()) + .setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue()) + .setDatabase(redisProperties.getDatabase()) + .setPassword(StrUtil.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null) + .setTimeout(singleServerConfig.getTimeout()) + .setRetryAttempts(singleServerConfig.getRetryAttempts()) + .setRetryInterval(singleServerConfig.getRetryInterval()) + .setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection()) + .setClientName(singleServerConfig.getClientName()) + .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) + .setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize()) + .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) + .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) + .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()) + .setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval()); + RedissonClient redissonClient = Redisson.create(config); + log.info("初始化 redis 配置"); + return redissonClient; + } + + @Bean + public DefaultRedisScript limitScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() { + return StrUtil.builder() + .append("local key = KEYS[1]\n") + .append("local count = tonumber(ARGV[1])\n") + .append("local time = tonumber(ARGV[2])\n") + .append("local current = redis.call('get', key);\n") + .append("if current and tonumber(current) > count then\n") + .append(" return current;\n") + .append("end\n") + .append("current = redis.call('incr', key)\n") + .append("if tonumber(current) == 1 then\n") + .append(" redis.call('expire', key, time)\n") + .append("end\n") + .append("return current;") + .toString(); + } + +} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/ComplexRouteDefinitionRepository.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/ComplexRouteDefinitionRepository.java deleted file mode 100644 index 70af9a671..000000000 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/ComplexRouteDefinitionRepository.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.ruoyi.gateway.config.provider; - -import org.springframework.cloud.gateway.route.RouteDefinition; -import org.springframework.cloud.gateway.route.RouteDefinitionRepository; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.List; -import java.util.Objects; - -/** - * 复合的路由存储器 - * - * @author Wenchao Gong - * @date 2021-09-11 - */ -public class ComplexRouteDefinitionRepository implements RouteDefinitionRepository { - - private RouteDefinitionRepository localCache; - private RouteDefinitionRepository remoteReps; - - public ComplexRouteDefinitionRepository(RouteDefinitionRepository localCache, RouteDefinitionRepository remoteReps) - { - this.localCache = localCache; - this.remoteReps = remoteReps; - } - - - @Override - public Flux getRouteDefinitions() - { - final Flux routeDefinitions = localCache.getRouteDefinitions(); - return routeDefinitions.collectList().flatMapIterable(list -> { - if(list.isEmpty()) { - final Flux definitions = remoteReps.getRouteDefinitions(); - definitions.collectList().flatMapIterable(routes -> { - if(!routes.isEmpty()) { - routes.stream().map(m -> { - routes.add(m); - return Mono.just(m); - }).forEach(m -> { - localCache.save(m); - }); - } - return routes; - }).filter(l -> Objects.nonNull(l)).map(m -> list.add(m)).collectList(); - } - return list; - }); - } - - @Override - public Mono save(Mono route) - { - remoteReps.save(route); - return localCache.save(route); - } - - @Override - public Mono delete(Mono routeId) - { - remoteReps.delete(routeId); - return localCache.delete(routeId); - } -} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java index fd8fc1beb..e1f1a0726 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java @@ -1,40 +1,41 @@ package com.ruoyi.gateway.config.provider; +import org.redisson.api.RMap; +import org.redisson.api.RedissonClient; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionRepository; -import org.springframework.data.redis.core.RedisTemplate; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.List; - /** * Redis 路由仓库 * * @author Wenchao Gong * @date 2021-09-11 */ -public class RedisRouteDefinitionRepository implements RouteDefinitionRepository { +public class RedisRouteDefinitionRepository implements RouteDefinitionRepository +{ public static final String KEY_ROUTES = "ROUTES::"; - private final RedisTemplate redisTemplate; + private final RedissonClient redissonClient; - public RedisRouteDefinitionRepository(RedisTemplate redisTemplate) + public RedisRouteDefinitionRepository(RedissonClient redissonClient) { - this.redisTemplate = redisTemplate; + this.redissonClient = redissonClient; } @Override public Flux getRouteDefinitions() { - List values = redisTemplate.opsForHash().values(KEY_ROUTES); - return Flux.fromIterable(values); + RMap map = redissonClient.getMap(KEY_ROUTES); + return Flux.fromIterable(map.values()); } @Override public Mono save(Mono route) { return route.flatMap(definition -> { - redisTemplate.opsForHash().put(KEY_ROUTES, definition.getId(), definition); + RMap map = redissonClient.getMap(KEY_ROUTES); + map.put(definition.getId(), definition); return Mono.empty(); }); } @@ -43,7 +44,8 @@ public class RedisRouteDefinitionRepository implements RouteDefinitionRepository public Mono delete(Mono routeId) { return routeId.flatMap(id -> { - redisTemplate.opsForHash().delete(KEY_ROUTES, id); + RMap map = redissonClient.getMap(KEY_ROUTES); + map.remove(id); return Mono.empty(); }); } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedissonProperties.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedissonProperties.java new file mode 100644 index 000000000..9a74bc703 --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedissonProperties.java @@ -0,0 +1,134 @@ +package com.ruoyi.gateway.config.provider; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.redisson.config.TransportMode; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * Redisson 配置属性 + * + * @author Lion Li + */ +@Data +@Component +@ConfigurationProperties(prefix = "redisson") +public class RedissonProperties +{ + + /** + * 线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int threads; + + /** + * Netty线程池数量,默认值 = 当前处理核数量 * 2 + */ + private int nettyThreads; + + /** + * 传输模式 + */ + private TransportMode transportMode; + + /** + * 单机服务配置 + */ + private SingleServerConfig singleServerConfig; + + /** + * 缓存组 + */ + private List cacheGroup; + + @Data + @NoArgsConstructor + public static class SingleServerConfig { + + /** + * 客户端名称 + */ + private String clientName; + + /** + * 最小空闲连接数 + */ + private int connectionMinimumIdleSize; + + /** + * 连接池大小 + */ + private int connectionPoolSize; + + /** + * 连接空闲超时,单位:毫秒 + */ + private int idleConnectionTimeout; + + /** + * 命令等待超时,单位:毫秒 + */ + private int timeout; + + /** + * 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + */ + private int retryAttempts; + + /** + * 命令重试发送时间间隔,单位:毫秒 + */ + private int retryInterval; + + /** + * 发布和订阅连接的最小空闲连接数 + */ + private int subscriptionConnectionMinimumIdleSize; + + /** + * 发布和订阅连接池大小 + */ + private int subscriptionConnectionPoolSize; + + /** + * 单个连接最大订阅数量 + */ + private int subscriptionsPerConnection; + + /** + * DNS监测时间间隔,单位:毫秒 + */ + private int dnsMonitoringInterval; + + } + + @Data + @NoArgsConstructor + public static class CacheGroup { + + /** + * 组id + */ + private String groupId; + + /** + * 组过期时间 + */ + private long ttl; + + /** + * 组最大空闲时间 + */ + private long maxIdleTime; + + /** + * 组最大长度 + */ + private int maxSize; + + } + +} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-dev.yml index c94a3d34f..d1e0c347d 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-dev.yml +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-dev.yml @@ -29,6 +29,38 @@ spring: # 是否开启ssl ssl: false +redisson: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 传输模式 + transportMode: "NIO" + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${ruoyi.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + retryAttempts: 3 + # 命令重试发送时间间隔,单位:毫秒 + retryInterval: 1500 + # 发布和订阅连接的最小空闲连接数 + subscriptionConnectionMinimumIdleSize: 1 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + # 单个连接最大订阅数量 + subscriptionsPerConnection: 5 + # DNS监测时间间隔,单位:毫秒 + dnsMonitoringInterval: 5000 + --- # Actuator 监控端点的配置项 management: endpoints: @@ -41,4 +73,4 @@ management: include: @endpoints.include@ endpoint: logfile: - external-file: ./logs/sys-console.log \ No newline at end of file + external-file: ./logs/sys-console.log diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-local.yml b/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-local.yml new file mode 100644 index 000000000..a59c5c023 --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application-local.yml @@ -0,0 +1,76 @@ +--- # 监控配置 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + # 增加客户端开关 + enabled: true + # 设置 Spring Boot Admin Server 地址 + url: http://localhost:9090/admin + instance: + prefer-ip: true # 注册实例时,优先使用 IP + username: ruoyi + password: 123456 + +--- # redis 配置 +spring: + redis: + # 地址 + host: 127.0.0.1 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码 + password: + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl: false + +redisson: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 传输模式 + transportMode: "NIO" + # 单节点配置 + singleServerConfig: + # 客户端名称 + clientName: ${ruoyi.name} + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 如果尝试在此限制之内发送成功,则开始启用 timeout 计时。 + retryAttempts: 3 + # 命令重试发送时间间隔,单位:毫秒 + retryInterval: 1500 + # 发布和订阅连接的最小空闲连接数 + subscriptionConnectionMinimumIdleSize: 1 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + # 单个连接最大订阅数量 + subscriptionsPerConnection: 5 + # DNS监测时间间隔,单位:毫秒 + dnsMonitoringInterval: 5000 + +--- # Actuator 监控端点的配置项 +management: + endpoints: + web: + # Actuator 提供的 API 接口的根目录。默认为 /actuator + base-path: /actuator + exposure: + # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + # 生产环境不建议放开所有 根据项目需求放开即可 + include: @endpoints.include@ + endpoint: + logfile: + external-file: ./logs/sys-console.log diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application.yml b/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application.yml index 9e1aa773c..99a081b1e 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application.yml +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/resources/application.yml @@ -1,3 +1,15 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi-Vue-Plus + # 版本 + version: ${ruoyi-vue-plus.version} + # 版权年份 + copyrightYear: 2021 + # 实例演示开关 + demoEnabled: true + # 获取ip地址开关 + addressEnabled: true server: port: 8090 # Spring配置