diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/config/RedissonConfig.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/config/RedissonConfig.java index d9897c42..1e15eace 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/config/RedissonConfig.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/config/RedissonConfig.java @@ -3,6 +3,7 @@ package com.iflytek.skillhub.config; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; +import org.redisson.config.SentinelServersConfig; import org.redisson.config.SingleServerConfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; @@ -10,6 +11,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; +import java.util.List; + @Configuration @ConditionalOnProperty(prefix = "skillhub.security.scanner", name = "enabled", havingValue = "true") public class RedissonConfig { @@ -21,36 +24,74 @@ public class RedissonConfig { static Config createConfig(RedisProperties redisProperties) { Config config = new Config(); + if (hasSentinelConfiguration(redisProperties)) { + configureSentinelServers(config, redisProperties); + return config; + } + SingleServerConfig singleServerConfig = config.useSingleServer() .setAddress(resolveAddress(redisProperties)) .setDatabase(redisProperties.getDatabase()); - if (StringUtils.hasText(redisProperties.getUsername())) { - singleServerConfig.setUsername(redisProperties.getUsername()); - } - if (StringUtils.hasText(redisProperties.getPassword())) { - singleServerConfig.setPassword(redisProperties.getPassword()); - } - if (StringUtils.hasText(redisProperties.getClientName())) { - singleServerConfig.setClientName(redisProperties.getClientName()); - } - if (redisProperties.getTimeout() != null) { - singleServerConfig.setTimeout(Math.toIntExact(redisProperties.getTimeout().toMillis())); - } - if (redisProperties.getConnectTimeout() != null) { - singleServerConfig.setConnectTimeout(Math.toIntExact(redisProperties.getConnectTimeout().toMillis())); - } + applySharedSettings(singleServerConfig, redisProperties); return config; } + private static void configureSentinelServers(Config config, RedisProperties redisProperties) { + SentinelServersConfig sentinelServersConfig = config.useSentinelServers() + .setMasterName(redisProperties.getSentinel().getMaster()) + .setDatabase(redisProperties.getDatabase()); + List nodes = redisProperties.getSentinel().getNodes(); + nodes.stream() + .map(String::trim) + .filter(StringUtils::hasText) + .map(node -> withRedisScheme(node, redisProperties)) + .forEach(sentinelServersConfig::addSentinelAddress); + + applySharedSettings(sentinelServersConfig, redisProperties); + } + + private static boolean hasSentinelConfiguration(RedisProperties redisProperties) { + return redisProperties.getSentinel() != null + && StringUtils.hasText(redisProperties.getSentinel().getMaster()) + && redisProperties.getSentinel().getNodes() != null + && !redisProperties.getSentinel().getNodes().isEmpty(); + } + + private static void applySharedSettings(org.redisson.config.BaseConfig serverConfig, + RedisProperties redisProperties) { + if (StringUtils.hasText(redisProperties.getUsername())) { + serverConfig.setUsername(redisProperties.getUsername()); + } + if (StringUtils.hasText(redisProperties.getPassword())) { + serverConfig.setPassword(redisProperties.getPassword()); + } + if (StringUtils.hasText(redisProperties.getClientName())) { + serverConfig.setClientName(redisProperties.getClientName()); + } + if (redisProperties.getTimeout() != null) { + serverConfig.setTimeout(Math.toIntExact(redisProperties.getTimeout().toMillis())); + } + if (redisProperties.getConnectTimeout() != null) { + serverConfig.setConnectTimeout(Math.toIntExact(redisProperties.getConnectTimeout().toMillis())); + } + } + private static String resolveAddress(RedisProperties redisProperties) { if (StringUtils.hasText(redisProperties.getUrl())) { return redisProperties.getUrl(); } + return withRedisScheme(redisProperties.getHost() + ":" + redisProperties.getPort(), redisProperties); + } + + private static String withRedisScheme(String address, RedisProperties redisProperties) { + if (address.startsWith("redis://") || address.startsWith("rediss://")) { + return address; + } String scheme = redisProperties.getSsl() != null && redisProperties.getSsl().isEnabled() ? "rediss" : "redis"; - return scheme + "://" + redisProperties.getHost() + ":" + redisProperties.getPort(); + return scheme + "://" + address; } } diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/RedissonConfigTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/RedissonConfigTest.java index 39d9e351..4ebb502d 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/RedissonConfigTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/config/RedissonConfigTest.java @@ -2,10 +2,13 @@ package com.iflytek.skillhub.config; import org.junit.jupiter.api.Test; import org.redisson.config.Config; +import org.redisson.config.SentinelServersConfig; import org.redisson.config.SingleServerConfig; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import java.lang.reflect.Method; import java.time.Duration; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -59,4 +62,52 @@ class RedissonConfigTest { assertThat(serverConfig.getTimeout()).isEqualTo(3000); assertThat(serverConfig.getConnectTimeout()).isEqualTo(5000); } + + @Test + void createConfig_usesSentinelServersWhenSentinelPropertiesArePresent() throws Exception { + RedisProperties properties = new RedisProperties(); + RedisProperties.Sentinel sentinel = new RedisProperties.Sentinel(); + sentinel.setMaster("mymaster"); + sentinel.setNodes(List.of("redis-sentinel-1:26379", "redis-sentinel-2:26379")); + properties.setSentinel(sentinel); + properties.setDatabase(3); + properties.setPassword("secret"); + properties.setClientName("skillhub-stream"); + properties.setTimeout(Duration.ofSeconds(3)); + properties.setConnectTimeout(Duration.ofSeconds(5)); + + Config config = RedissonConfig.createConfig(properties); + SentinelServersConfig sentinelConfig = sentinelConfig(config); + + assertThat(config.isSentinelConfig()).isTrue(); + assertThat(sentinelConfig.getMasterName()).isEqualTo("mymaster"); + assertThat(sentinelConfig.getSentinelAddresses()) + .containsExactly("redis://redis-sentinel-1:26379", "redis://redis-sentinel-2:26379"); + assertThat(sentinelConfig.getDatabase()).isEqualTo(3); + assertThat(sentinelConfig.getPassword()).isEqualTo("secret"); + assertThat(sentinelConfig.getClientName()).isEqualTo("skillhub-stream"); + assertThat(sentinelConfig.getTimeout()).isEqualTo(3000); + assertThat(sentinelConfig.getConnectTimeout()).isEqualTo(5000); + } + + @Test + void createConfig_usesSecureSchemeForSentinelAddressesWhenSslEnabled() throws Exception { + RedisProperties properties = new RedisProperties(); + RedisProperties.Sentinel sentinel = new RedisProperties.Sentinel(); + sentinel.setMaster("mymaster"); + sentinel.setNodes(List.of("redis-sentinel-1:26379")); + properties.setSentinel(sentinel); + properties.getSsl().setEnabled(true); + + Config config = RedissonConfig.createConfig(properties); + SentinelServersConfig sentinelConfig = sentinelConfig(config); + + assertThat(sentinelConfig.getSentinelAddresses()).containsExactly("rediss://redis-sentinel-1:26379"); + } + + private SentinelServersConfig sentinelConfig(Config config) throws Exception { + Method method = Config.class.getDeclaredMethod("getSentinelServersConfig"); + method.setAccessible(true); + return (SentinelServersConfig) method.invoke(config); + } }