Polish "Add support for static master-replica with Lettuce"
See gh-46957
This commit is contained in:
parent
c280fdefe8
commit
fbcc1fdec6
|
@ -75,7 +75,10 @@ spring:
|
|||
TIP: You can also register an arbitrary number of beans that implement javadoc:org.springframework.boot.data.redis.autoconfigure.LettuceClientConfigurationBuilderCustomizer[] for more advanced customizations.
|
||||
javadoc:io.lettuce.core.resource.ClientResources[] can also be customized using javadoc:org.springframework.boot.data.redis.autoconfigure.ClientResourcesBuilderCustomizer[].
|
||||
If you use Jedis, javadoc:org.springframework.boot.data.redis.autoconfigure.JedisClientConfigurationBuilderCustomizer[] is also available.
|
||||
Alternatively, you can register a bean of type javadoc:org.springframework.data.redis.connection.RedisStandaloneConfiguration[], javadoc:org.springframework.data.redis.connection.RedisSentinelConfiguration[], or javadoc:org.springframework.data.redis.connection.RedisClusterConfiguration[] to take full control over the configuration.
|
||||
|
||||
Alternatively, you can register a bean of type javadoc:org.springframework.data.redis.connection.RedisStandaloneConfiguration[], javadoc:org.springframework.data.redis.connection.RedisSentinelConfiguration[], javadoc:org.springframework.data.redis.connection.RedisClusterConfiguration[], or javadoc:org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration[]] to take full control over the configuration.
|
||||
|
||||
NOTE: master/replica is not supported by Jedis.
|
||||
|
||||
If you add your own javadoc:org.springframework.context.annotation.Bean[format=annotation] of any of the auto-configured types, it replaces the default (except in the case of javadoc:org.springframework.data.redis.core.RedisTemplate[], when the exclusion is based on the bean name, `redisTemplate`, not its type).
|
||||
|
||||
|
|
|
@ -33,9 +33,9 @@ import org.springframework.data.redis.connection.RedisNode;
|
|||
import org.springframework.data.redis.connection.RedisPassword;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Base Redis connection configuration.
|
||||
|
@ -49,7 +49,6 @@ import org.springframework.util.CollectionUtils;
|
|||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Yanming Zhou
|
||||
* @author Yong-Hyun Kim
|
||||
*/
|
||||
abstract class DataRedisConnectionConfiguration {
|
||||
|
||||
|
@ -64,6 +63,8 @@ abstract class DataRedisConnectionConfiguration {
|
|||
|
||||
private final @Nullable RedisClusterConfiguration clusterConfiguration;
|
||||
|
||||
private final @Nullable RedisStaticMasterReplicaConfiguration masterReplicaConfiguration;
|
||||
|
||||
private final DataRedisConnectionDetails connectionDetails;
|
||||
|
||||
protected final Mode mode;
|
||||
|
@ -72,11 +73,13 @@ abstract class DataRedisConnectionConfiguration {
|
|||
DataRedisConnectionDetails connectionDetails,
|
||||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
|
||||
ObjectProvider<RedisStaticMasterReplicaConfiguration> masterReplicaConfiguration) {
|
||||
this.properties = properties;
|
||||
this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable();
|
||||
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
|
||||
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
|
||||
this.masterReplicaConfiguration = masterReplicaConfiguration.getIfAvailable();
|
||||
this.connectionDetails = connectionDetails;
|
||||
this.mode = determineMode();
|
||||
}
|
||||
|
@ -145,6 +148,25 @@ abstract class DataRedisConnectionConfiguration {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected final @Nullable RedisStaticMasterReplicaConfiguration getMasterReplicaConfiguration() {
|
||||
if (this.masterReplicaConfiguration != null) {
|
||||
return this.masterReplicaConfiguration;
|
||||
}
|
||||
if (this.connectionDetails.getMasterReplica() != null) {
|
||||
List<Node> nodes = this.connectionDetails.getMasterReplica().getNodes();
|
||||
RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration(
|
||||
nodes.get(0).host(), nodes.get(0).port());
|
||||
nodes.stream().skip(1).forEach((node) -> config.addNode(node.host(), node.port()));
|
||||
config.setUsername(this.connectionDetails.getUsername());
|
||||
String password = this.connectionDetails.getPassword();
|
||||
if (password != null) {
|
||||
config.setPassword(RedisPassword.of(password));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<RedisNode> getNodes(Cluster cluster) {
|
||||
return cluster.getNodes().stream().map(this::asRedisNode).toList();
|
||||
}
|
||||
|
@ -193,15 +215,15 @@ abstract class DataRedisConnectionConfiguration {
|
|||
if (getClusterConfiguration() != null) {
|
||||
return Mode.CLUSTER;
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(this.properties.getLettuce().getNodes())) {
|
||||
return Mode.STATIC_MASTER_REPLICA;
|
||||
if (getMasterReplicaConfiguration() != null) {
|
||||
return Mode.MASTER_REPLICA;
|
||||
}
|
||||
return Mode.STANDALONE;
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
|
||||
STANDALONE, CLUSTER, SENTINEL, STATIC_MASTER_REPLICA
|
||||
STANDALONE, CLUSTER, MASTER_REPLICA, SENTINEL
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -58,8 +58,8 @@ public interface DataRedisConnectionDetails extends ConnectionDetails {
|
|||
}
|
||||
|
||||
/**
|
||||
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()} and
|
||||
* {@link #getCluster()}.
|
||||
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()},
|
||||
* {@link #getCluster()} and {@link #getMasterReplica()}.
|
||||
* @return the Redis standalone configuration
|
||||
*/
|
||||
default @Nullable Standalone getStandalone() {
|
||||
|
@ -67,8 +67,8 @@ public interface DataRedisConnectionDetails extends ConnectionDetails {
|
|||
}
|
||||
|
||||
/**
|
||||
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()} and
|
||||
* {@link #getCluster()}.
|
||||
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()},
|
||||
* {@link #getCluster()} and {@link #getMasterReplica()}.
|
||||
* @return the Redis sentinel configuration
|
||||
*/
|
||||
default @Nullable Sentinel getSentinel() {
|
||||
|
@ -76,14 +76,23 @@ public interface DataRedisConnectionDetails extends ConnectionDetails {
|
|||
}
|
||||
|
||||
/**
|
||||
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()} and
|
||||
* {@link #getSentinel()}.
|
||||
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()},
|
||||
* {@link #getSentinel()} and {@link #getMasterReplica()}.
|
||||
* @return the Redis cluster configuration
|
||||
*/
|
||||
default @Nullable Cluster getCluster() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis master replica configuration. Mutually exclusive with
|
||||
* {@link #getStandalone()}, {@link #getSentinel()} and {@link #getCluster()}.
|
||||
* @return the Redis master replica configuration
|
||||
*/
|
||||
default @Nullable MasterReplica getMasterReplica() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis standalone configuration.
|
||||
*/
|
||||
|
@ -201,6 +210,20 @@ public interface DataRedisConnectionDetails extends ConnectionDetails {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis master replica configuration.
|
||||
*/
|
||||
interface MasterReplica {
|
||||
|
||||
/**
|
||||
* Static nodes to use. This represents the full list of cluster nodes and is
|
||||
* required to have at least one entry.
|
||||
* @return the nodes to use
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in a sentinel or cluster configuration.
|
||||
*
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||
* @author Stephane Nicoll
|
||||
* @author Scott Frederick
|
||||
* @author Yanming Zhou
|
||||
* @author Yong-Hyun Kim
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.data.redis")
|
||||
|
@ -95,6 +94,8 @@ public class DataRedisProperties {
|
|||
|
||||
private @Nullable Cluster cluster;
|
||||
|
||||
private @Nullable Masterreplica masterreplica;
|
||||
|
||||
private final Ssl ssl = new Ssl();
|
||||
|
||||
private final Jedis jedis = new Jedis();
|
||||
|
@ -201,6 +202,14 @@ public class DataRedisProperties {
|
|||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
public @Nullable Masterreplica getMasterreplica() {
|
||||
return this.masterreplica;
|
||||
}
|
||||
|
||||
public void setMasterreplica(@Nullable Masterreplica masterreplica) {
|
||||
this.masterreplica = masterreplica;
|
||||
}
|
||||
|
||||
public Jedis getJedis() {
|
||||
return this.jedis;
|
||||
}
|
||||
|
@ -355,6 +364,26 @@ public class DataRedisProperties {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Master Replica properties.
|
||||
*/
|
||||
public static class Masterreplica {
|
||||
|
||||
/**
|
||||
* Static list of "host:port" pairs to use, at least one entry is required.
|
||||
*/
|
||||
private @Nullable List<String> nodes;
|
||||
|
||||
public @Nullable List<String> getNodes() {
|
||||
return this.nodes;
|
||||
}
|
||||
|
||||
public void setNodes(@Nullable List<String> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis sentinel properties.
|
||||
*/
|
||||
|
@ -483,20 +512,6 @@ public class DataRedisProperties {
|
|||
|
||||
private final Cluster cluster = new Cluster();
|
||||
|
||||
/**
|
||||
* List of static master-replica "host:port" pairs regardless of role
|
||||
* as the actual roles are determined by querying each node's ROLE command.
|
||||
*/
|
||||
private @Nullable List<String> nodes;
|
||||
|
||||
public @Nullable List<String> getNodes() {
|
||||
return this.nodes;
|
||||
}
|
||||
|
||||
public void setNodes(@Nullable List<String> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public Duration getShutdownTimeout() {
|
||||
return this.shutdownTimeout;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
|||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisSslClientConfigurationBuilder;
|
||||
|
@ -66,9 +67,10 @@ class JedisConnectionConfiguration extends DataRedisConnectionConfiguration {
|
|||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
|
||||
ObjectProvider<RedisStaticMasterReplicaConfiguration> masterReplicaConfiguration,
|
||||
DataRedisConnectionDetails connectionDetails) {
|
||||
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfiguration,
|
||||
clusterConfiguration);
|
||||
clusterConfiguration, masterReplicaConfiguration);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -104,7 +106,7 @@ class JedisConnectionConfiguration extends DataRedisConnectionConfiguration {
|
|||
Assert.state(sentinelConfig != null, "'sentinelConfig' must not be null");
|
||||
yield new JedisConnectionFactory(sentinelConfig, clientConfiguration);
|
||||
}
|
||||
case STATIC_MASTER_REPLICA -> throw new IllegalStateException("Static master replica is not supported for Jedis");
|
||||
case MASTER_REPLICA -> throw new IllegalStateException("'masterReplicaConfig' is not supported by Jedis");
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
package org.springframework.boot.data.redis.autoconfigure;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.lettuce.core.ClientOptions;
|
||||
import io.lettuce.core.ReadFrom;
|
||||
|
@ -39,8 +37,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
|
||||
import org.springframework.boot.data.redis.autoconfigure.RedisConnectionDetails.Node;
|
||||
import org.springframework.boot.data.redis.autoconfigure.RedisProperties.Lettuce;
|
||||
import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties.Lettuce.Cluster.Refresh;
|
||||
import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties.Pool;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
|
@ -59,7 +55,6 @@ import org.springframework.data.redis.connection.lettuce.LettuceClientConfigurat
|
|||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -70,7 +65,6 @@ import org.springframework.util.StringUtils;
|
|||
* @author Moritz Halbritter
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @author Yong-Hyun Kim
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RedisClient.class)
|
||||
|
@ -81,9 +75,10 @@ class LettuceConnectionConfiguration extends DataRedisConnectionConfiguration {
|
|||
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
|
||||
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
|
||||
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
|
||||
ObjectProvider<RedisStaticMasterReplicaConfiguration> masterReplicaConfiguration,
|
||||
DataRedisConnectionDetails connectionDetails) {
|
||||
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfigurationProvider,
|
||||
clusterConfigurationProvider);
|
||||
clusterConfigurationProvider, masterReplicaConfiguration);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
|
@ -127,12 +122,6 @@ class LettuceConnectionConfiguration extends DataRedisConnectionConfiguration {
|
|||
LettuceClientConfiguration clientConfiguration = getLettuceClientConfiguration(
|
||||
clientConfigurationBuilderCustomizers, clientOptionsBuilderCustomizers, clientResources,
|
||||
getProperties().getLettuce().getPool());
|
||||
|
||||
RedisStaticMasterReplicaConfiguration staticMasterReplicaConfiguration = getStaticMasterReplicaConfiguration();
|
||||
if (staticMasterReplicaConfiguration != null) {
|
||||
return new LettuceConnectionFactory(staticMasterReplicaConfiguration, clientConfiguration);
|
||||
}
|
||||
|
||||
return switch (this.mode) {
|
||||
case STANDALONE -> new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
|
||||
case CLUSTER -> {
|
||||
|
@ -145,34 +134,14 @@ class LettuceConnectionConfiguration extends DataRedisConnectionConfiguration {
|
|||
Assert.state(sentinelConfig != null, "'sentinelConfig' must not be null");
|
||||
yield new LettuceConnectionFactory(sentinelConfig, clientConfiguration);
|
||||
}
|
||||
case STATIC_MASTER_REPLICA -> {
|
||||
RedisStaticMasterReplicaConfiguration configuration = getStaticMasterReplicaConfiguration();
|
||||
Assert.state(configuration != null, "'staticMasterReplicaConfiguration' must not be null");
|
||||
yield new LettuceConnectionFactory(configuration, clientConfiguration);
|
||||
case MASTER_REPLICA -> {
|
||||
RedisStaticMasterReplicaConfiguration masterReplicaConfiguration = getMasterReplicaConfiguration();
|
||||
Assert.state(masterReplicaConfiguration != null, "'masterReplicaConfig' must not be null");
|
||||
yield new LettuceConnectionFactory(masterReplicaConfiguration, clientConfiguration);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private @Nullable RedisStaticMasterReplicaConfiguration getStaticMasterReplicaConfiguration() {
|
||||
RedisProperties.Lettuce lettuce = getProperties().getLettuce();
|
||||
|
||||
if (!CollectionUtils.isEmpty(lettuce.getNodes())) {
|
||||
List<Node> nodes = asNodes(lettuce.getNodes());
|
||||
RedisStaticMasterReplicaConfiguration configuration = new RedisStaticMasterReplicaConfiguration(
|
||||
nodes.get(0).host(), nodes.get(0).port());
|
||||
configuration.setUsername(getProperties().getUsername());
|
||||
if (StringUtils.hasText(getProperties().getPassword())) {
|
||||
configuration.setPassword(getProperties().getPassword());
|
||||
}
|
||||
configuration.setDatabase(getProperties().getDatabase());
|
||||
nodes.stream().skip(1).forEach((node) -> configuration.addNode(node.host(), node.port()));
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private LettuceClientConfiguration getLettuceClientConfiguration(
|
||||
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> clientConfigurationBuilderCustomizers,
|
||||
ObjectProvider<LettuceClientOptionsBuilderCustomizer> clientOptionsBuilderCustomizers,
|
||||
|
@ -288,20 +257,6 @@ class LettuceConnectionConfiguration extends DataRedisConnectionConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
private List<Node> asNodes(@Nullable List<String> nodes) {
|
||||
if (nodes == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return nodes.stream().map(this::asNode).toList();
|
||||
}
|
||||
|
||||
private Node asNode(String node) {
|
||||
int portSeparatorIndex = node.lastIndexOf(':');
|
||||
String host = node.substring(0, portSeparatorIndex);
|
||||
int port = Integer.parseInt(node.substring(portSeparatorIndex + 1));
|
||||
return new Node(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to allow optional commons-pool2 dependency.
|
||||
*/
|
||||
|
|
|
@ -92,6 +92,12 @@ class PropertiesDataRedisConnectionDetails implements DataRedisConnectionDetails
|
|||
return (cluster != null) ? new PropertiesCluster(cluster) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable MasterReplica getMasterReplica() {
|
||||
DataRedisProperties.Masterreplica masterreplica = this.properties.getMasterreplica();
|
||||
return (masterreplica != null) ? new PropertiesMasterReplica(masterreplica) : null;
|
||||
}
|
||||
|
||||
private @Nullable DataRedisUrl getRedisUrl() {
|
||||
return DataRedisUrl.of(this.properties.getUrl());
|
||||
}
|
||||
|
@ -128,6 +134,24 @@ class PropertiesDataRedisConnectionDetails implements DataRedisConnectionDetails
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MasterReplica} implementation backed by properties.
|
||||
*/
|
||||
private class PropertiesMasterReplica implements MasterReplica {
|
||||
|
||||
private final List<Node> nodes;
|
||||
|
||||
PropertiesMasterReplica(DataRedisProperties.Masterreplica properties) {
|
||||
this.nodes = asNodes(properties.getNodes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return this.nodes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Sentinel} implementation backed by properties.
|
||||
*/
|
||||
|
|
|
@ -247,6 +247,15 @@ class DataRedisAutoConfigurationJedisTests {
|
|||
.isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWitMasterReplica() {
|
||||
this.contextRunner.withPropertyValues("spring.data.redis.masterreplica.nodes=127.0.0.1:27379,127.0.0.1:27380")
|
||||
.run((context) -> assertThat(context).hasFailed()
|
||||
.getFailure()
|
||||
.rootCause()
|
||||
.hasMessageContaining("'masterReplicaConfig' is not supported by Jedis"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithSslEnabled() {
|
||||
this.contextRunner.withPropertyValues("spring.data.redis.ssl.enabled:true").run((context) -> {
|
||||
|
|
|
@ -61,6 +61,7 @@ import org.springframework.data.redis.connection.RedisNode;
|
|||
import org.springframework.data.redis.connection.RedisPassword;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder;
|
||||
|
@ -91,7 +92,6 @@ import static org.mockito.Mockito.mock;
|
|||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Yong-Hyun Kim
|
||||
*/
|
||||
class DataRedisAutoConfigurationTests {
|
||||
|
||||
|
@ -497,40 +497,38 @@ class DataRedisAutoConfigurationTests {
|
|||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(getUserName(connectionFactory)).isEqualTo("user");
|
||||
assertThat(connectionFactory.getPassword()).isEqualTo("password");
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithStaticMasterReplica() {
|
||||
List<String> staticMasterReplicaNodes = Arrays.asList("127.0.0.1:28319", "127.0.0.1:28320", "[::1]:28321");
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.data.redis.lettuce.static-master-replica.nodes[0]:" + staticMasterReplicaNodes.get(0),
|
||||
"spring.data.redis.lettuce.static-master-replica.nodes[1]:" + staticMasterReplicaNodes.get(1),
|
||||
"spring.data.redis.lettuce.static-master-replica.nodes[2]:" + staticMasterReplicaNodes.get(2))
|
||||
.run((context) -> {
|
||||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(connectionFactory.getSentinelConfiguration()).isNull();
|
||||
assertThat(connectionFactory.getClusterConfiguration()).isNull();
|
||||
assertThat(isStaticMasterReplicaAware(connectionFactory)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithStaticMasterReplicaAndAuthenticationAndDatabase() {
|
||||
List<String> staticMasterReplicaNodes = Arrays.asList("127.0.0.1:28319", "127.0.0.1:28320");
|
||||
void testRedisConfigurationWithMasterReplica() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.data.redis.masterreplica.nodes=127.0.0.1:28319,127.0.0.1:28320,[::1]:28321")
|
||||
.run((context) -> {
|
||||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(connectionFactory.getSentinelConfiguration()).isNull();
|
||||
assertThat(connectionFactory.getClusterConfiguration()).isNull();
|
||||
assertThat(connectionFactory).extracting("configuration")
|
||||
.isInstanceOfSatisfying(RedisStaticMasterReplicaConfiguration.class,
|
||||
(masterReplicaConfiguration) -> assertThat(masterReplicaConfiguration.getNodes()
|
||||
.stream()
|
||||
.map((config) -> new RedisNode(config.getHostName(), config.getPort())))
|
||||
.containsExactly(new RedisNode("127.0.0.1", 28319), new RedisNode("127.0.0.1", 28320),
|
||||
new RedisNode("[::1]", 28321)));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithMasterAndAuthentication() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.data.redis.username=user", "spring.data.redis.password=password",
|
||||
"spring.data.redis.database=1",
|
||||
"spring.data.redis.lettuce.static-master-replica.nodes[0]:" + staticMasterReplicaNodes.get(0),
|
||||
"spring.data.redis.lettuce.static-master-replica.nodes[1]:" + staticMasterReplicaNodes.get(1))
|
||||
"spring.data.redis.masterreplica.nodes=127.0.0.1:28319,127.0.0.1:28320")
|
||||
.run((context) -> {
|
||||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(getUserName(connectionFactory)).isEqualTo("user");
|
||||
assertThat(connectionFactory.getPassword()).isEqualTo("password");
|
||||
assertThat(connectionFactory.getDatabase()).isOne();
|
||||
assertThat(connectionFactory).extracting("configuration")
|
||||
.isInstanceOf(RedisStaticMasterReplicaConfiguration.class);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -661,6 +659,27 @@ class DataRedisAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesMasterReplicaFromCustomConnectionDetails() {
|
||||
this.contextRunner.withUserConfiguration(ConnectionDetailsMasterReplicaConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(DataRedisConnectionDetails.class)
|
||||
.doesNotHaveBean(PropertiesDataRedisConnectionDetails.class);
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
assertThat(cf).extracting("configuration")
|
||||
.isInstanceOfSatisfying(RedisStaticMasterReplicaConfiguration.class,
|
||||
(masterReplicationConfiguration) -> {
|
||||
assertThat(masterReplicationConfiguration.getUsername()).isEqualTo("user-1");
|
||||
assertThat(masterReplicationConfiguration.getPassword().get())
|
||||
.isEqualTo("password-1".toCharArray());
|
||||
assertThat(masterReplicationConfiguration.getNodes())
|
||||
.map((nodeConfiguration) -> new RedisNode(nodeConfiguration.getHostName(),
|
||||
nodeConfiguration.getPort()))
|
||||
.containsExactly(new RedisNode("node-1", 12345), new RedisNode("node-2", 23456));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithSslEnabled() {
|
||||
this.contextRunner.withPropertyValues("spring.data.redis.ssl.enabled:true").run((context) -> {
|
||||
|
@ -738,10 +757,6 @@ class DataRedisAutoConfigurationTests {
|
|||
return node;
|
||||
}
|
||||
|
||||
private boolean isStaticMasterReplicaAware(LettuceConnectionFactory factory) {
|
||||
return ReflectionTestUtils.invokeMethod(factory, "isStaticMasterReplicaAware");
|
||||
}
|
||||
|
||||
private static final class RedisNodes implements Nodes {
|
||||
|
||||
private final List<RedisNodeDescription> descriptions;
|
||||
|
@ -921,4 +936,38 @@ class DataRedisAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ConnectionDetailsMasterReplicaConfiguration {
|
||||
|
||||
@Bean
|
||||
DataRedisConnectionDetails redisConnectionDetails() {
|
||||
return new DataRedisConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterReplica getMasterReplica() {
|
||||
return new MasterReplica() {
|
||||
|
||||
@Override
|
||||
public List<Node> getNodes() {
|
||||
return List.of(new Node("node-1", 12345), new Node("node-2", 23456));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ class PropertiesRedisConnectionDetailsTests {
|
|||
assertThat(standalone.getDatabase()).isEqualTo(0);
|
||||
assertThat(this.connectionDetails.getSentinel()).isNull();
|
||||
assertThat(this.connectionDetails.getCluster()).isNull();
|
||||
assertThat(this.connectionDetails.getMasterReplica()).isNull();
|
||||
assertThat(this.connectionDetails.getUsername()).isNull();
|
||||
assertThat(this.connectionDetails.getPassword()).isNull();
|
||||
}
|
||||
|
@ -147,6 +148,15 @@ class PropertiesRedisConnectionDetailsTests {
|
|||
new Node("127.0.0.1", 2222), new Node("[::1]", 3333));
|
||||
}
|
||||
|
||||
@Test
|
||||
void masterReplicaIsConfigured() {
|
||||
DataRedisProperties.Masterreplica masterReplica = new DataRedisProperties.Masterreplica();
|
||||
masterReplica.setNodes(List.of("localhost:1111", "127.0.0.1:2222", "[::1]:3333"));
|
||||
this.properties.setMasterreplica(masterReplica);
|
||||
assertThat(this.connectionDetails.getMasterReplica().getNodes()).containsExactly(new Node("localhost", 1111),
|
||||
new Node("127.0.0.1", 2222), new Node("[::1]", 3333));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sentinelIsConfigured() {
|
||||
DataRedisProperties.Sentinel sentinel = new DataRedisProperties.Sentinel();
|
||||
|
|
Loading…
Reference in New Issue