Skip to content

Commit b387a08

Browse files
committed
Support Redis static master-replica auto-configuration
Signed-off-by: 용현 <[email protected]>
1 parent c1d51f8 commit b387a08

File tree

9 files changed

+257
-14
lines changed

9 files changed

+257
-14
lines changed

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/JedisConnectionConfiguration.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.data.redis.connection.RedisConnectionFactory;
3939
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
4040
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
41+
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
4142
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
4243
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
4344
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisSslClientConfigurationBuilder;
@@ -55,6 +56,7 @@
5556
* @author Andy Wilkinson
5657
* @author Phillip Webb
5758
* @author Scott Frederick
59+
* @author Yong-Hyun Kim
5860
*/
5961
@Configuration(proxyBeanMethods = false)
6062
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
@@ -64,10 +66,12 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {
6466

6567
JedisConnectionConfiguration(RedisProperties properties,
6668
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
67-
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
68-
ObjectProvider<RedisClusterConfiguration> clusterConfiguration, RedisConnectionDetails connectionDetails) {
69-
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfiguration,
70-
clusterConfiguration);
69+
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
70+
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
71+
ObjectProvider<RedisStaticMasterReplicaConfiguration> staticMasterReplicaConfigurationProvider,
72+
RedisConnectionDetails connectionDetails) {
73+
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfigurationProvider,
74+
clusterConfigurationProvider, staticMasterReplicaConfigurationProvider);
7175
}
7276

7377
@Bean
@@ -103,6 +107,7 @@ private JedisConnectionFactory createJedisConnectionFactory(
103107
Assert.state(sentinelConfig != null, "'sentinelConfig' must not be null");
104108
yield new JedisConnectionFactory(sentinelConfig, clientConfiguration);
105109
}
110+
case STATIC_MASTER_REPLICA -> throw new IllegalStateException("Static master-replica mode is not supported by Jedis");
106111
};
107112
}
108113

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/LettuceConnectionConfiguration.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.data.redis.connection.RedisConnectionFactory;
5050
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
5151
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
52+
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
5253
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
5354
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder;
5455
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@@ -64,6 +65,7 @@
6465
* @author Moritz Halbritter
6566
* @author Phillip Webb
6667
* @author Scott Frederick
68+
* @author Yong-Hyun Kim
6769
*/
6870
@Configuration(proxyBeanMethods = false)
6971
@ConditionalOnClass(RedisClient.class)
@@ -74,9 +76,10 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
7476
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
7577
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
7678
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
79+
ObjectProvider<RedisStaticMasterReplicaConfiguration> staticMasterReplicaConfigurationProvider,
7780
RedisConnectionDetails connectionDetails) {
7881
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfigurationProvider,
79-
clusterConfigurationProvider);
82+
clusterConfigurationProvider, staticMasterReplicaConfigurationProvider);
8083
}
8184

8285
@Bean(destroyMethod = "shutdown")
@@ -132,6 +135,12 @@ private LettuceConnectionFactory createConnectionFactory(
132135
Assert.state(sentinelConfig != null, "'sentinelConfig' must not be null");
133136
yield new LettuceConnectionFactory(sentinelConfig, clientConfiguration);
134137
}
138+
case STATIC_MASTER_REPLICA -> {
139+
RedisStaticMasterReplicaConfiguration staticMasterReplicaConfiguration = getStaticMasterReplicaConfiguration();
140+
Assert.state(staticMasterReplicaConfiguration != null,
141+
"'staticMasterReplicaConfiguration' must not be null");
142+
yield new LettuceConnectionFactory(staticMasterReplicaConfiguration, clientConfiguration);
143+
}
135144
};
136145
}
137146

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/PropertiesRedisConnectionDetails.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* @author Scott Frederick
3636
* @author Yanming Zhou
3737
* @author Phillip Webb
38+
* @author Yong-Hyun Kim
3839
*/
3940
class PropertiesRedisConnectionDetails implements RedisConnectionDetails {
4041

@@ -92,6 +93,12 @@ public Standalone getStandalone() {
9293
return (cluster != null) ? new PropertiesCluster(cluster) : null;
9394
}
9495

96+
@Override
97+
public @Nullable StaticMasterReplica getStaticMasterReplica() {
98+
RedisProperties.StaticMasterReplica staticMasterReplica = this.properties.getStaticMasterReplica();
99+
return (staticMasterReplica != null) ? new PropertiesStaticMasterReplica(getStandalone().getDatabase(), staticMasterReplica) : null;
100+
}
101+
95102
private @Nullable RedisUrl getRedisUrl() {
96103
return RedisUrl.of(this.properties.getUrl());
97104
}
@@ -181,4 +188,35 @@ public List<Node> getNodes() {
181188

182189
}
183190

191+
/**
192+
* {@link StaticMasterReplica} implementation backed by properties.
193+
*/
194+
private class PropertiesStaticMasterReplica implements StaticMasterReplica {
195+
196+
private final int database;
197+
198+
private final RedisProperties.StaticMasterReplica properties;
199+
200+
PropertiesStaticMasterReplica(int database, RedisProperties.StaticMasterReplica properties) {
201+
this.database = database;
202+
this.properties = properties;
203+
}
204+
205+
@Override
206+
public int getDatabase() {
207+
return this.database;
208+
}
209+
210+
@Override
211+
public List<Node> getNodes() {
212+
return asNodes(this.properties.getNodes());
213+
}
214+
215+
@Override
216+
public @Nullable SslBundle getSslBundle() {
217+
return PropertiesRedisConnectionDetails.this.getSslBundle();
218+
}
219+
220+
}
221+
184222
}

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/RedisConnectionConfiguration.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.data.redis.connection.RedisPassword;
3434
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
3535
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
36+
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
3637
import org.springframework.util.Assert;
3738
import org.springframework.util.ClassUtils;
3839

@@ -48,6 +49,7 @@
4849
* @author Andy Wilkinson
4950
* @author Phillip Webb
5051
* @author Yanming Zhou
52+
* @author Yong-Hyun Kim
5153
*/
5254
abstract class RedisConnectionConfiguration {
5355

@@ -62,19 +64,23 @@ abstract class RedisConnectionConfiguration {
6264

6365
private final @Nullable RedisClusterConfiguration clusterConfiguration;
6466

67+
private final @Nullable RedisStaticMasterReplicaConfiguration staticMasterReplicaConfiguration;
68+
6569
private final RedisConnectionDetails connectionDetails;
6670

6771
protected final Mode mode;
6872

6973
protected RedisConnectionConfiguration(RedisProperties properties, RedisConnectionDetails connectionDetails,
7074
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
7175
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
72-
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
76+
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
77+
ObjectProvider<RedisStaticMasterReplicaConfiguration> staticMasterReplicaConfigurationProvider) {
7378
this.properties = properties;
7479
this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable();
7580
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
7681
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
7782
this.connectionDetails = connectionDetails;
83+
this.staticMasterReplicaConfiguration = staticMasterReplicaConfigurationProvider.getIfAvailable();
7884
this.mode = determineMode();
7985
}
8086

@@ -142,6 +148,31 @@ protected final RedisStandaloneConfiguration getStandaloneConfig() {
142148
return null;
143149
}
144150

151+
/**
152+
* Create a {@link RedisStaticMasterReplicaConfiguration} if necessary.
153+
* @return {@literal null} if no static master-replica settings are set.
154+
*/
155+
protected final @Nullable RedisStaticMasterReplicaConfiguration getStaticMasterReplicaConfiguration() {
156+
if (this.staticMasterReplicaConfiguration != null) {
157+
return this.staticMasterReplicaConfiguration;
158+
}
159+
if (this.connectionDetails.getStaticMasterReplica() != null) {
160+
List<Node> nodes = this.connectionDetails.getStaticMasterReplica().getNodes();
161+
Assert.notEmpty(nodes, "'staticMasterReplica.nodes' must not be empty'");
162+
Node firstNode = nodes.get(0);
163+
RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration(firstNode.host(), firstNode.port());
164+
nodes.stream().skip(1).forEach(node -> config.addNode(node.host(), node.port()));
165+
config.setUsername(this.connectionDetails.getUsername());
166+
String password = this.connectionDetails.getPassword();
167+
if (password != null) {
168+
config.setPassword(RedisPassword.of(password));
169+
}
170+
config.setDatabase(this.connectionDetails.getStaticMasterReplica().getDatabase());
171+
return config;
172+
}
173+
return null;
174+
}
175+
145176
private List<RedisNode> getNodes(Cluster cluster) {
146177
return cluster.getNodes().stream().map(this::asRedisNode).toList();
147178
}
@@ -162,6 +193,8 @@ protected final RedisProperties getProperties() {
162193
? this.connectionDetails.getCluster().getSslBundle() : null;
163194
case SENTINEL -> (this.connectionDetails.getSentinel() != null)
164195
? this.connectionDetails.getSentinel().getSslBundle() : null;
196+
case STATIC_MASTER_REPLICA -> (this.connectionDetails.getStaticMasterReplica() != null)
197+
? this.connectionDetails.getStaticMasterReplica().getSslBundle() : null;
165198
};
166199
}
167200

@@ -197,12 +230,15 @@ private Mode determineMode() {
197230
if (getClusterConfiguration() != null) {
198231
return Mode.CLUSTER;
199232
}
233+
if (getStaticMasterReplicaConfiguration() != null) {
234+
return Mode.STATIC_MASTER_REPLICA;
235+
}
200236
return Mode.STANDALONE;
201237
}
202238

203239
enum Mode {
204240

205-
STANDALONE, CLUSTER, SENTINEL
241+
STANDALONE, CLUSTER, SENTINEL, STATIC_MASTER_REPLICA
206242

207243
}
208244

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/RedisConnectionDetails.java

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
*
3030
* @author Moritz Halbritter
3131
* @author Andy Wilkinson
32+
* @author Yong-Hyun Kim
3233
* @since 4.0.0
3334
*/
3435
public interface RedisConnectionDetails extends ConnectionDetails {
@@ -50,32 +51,41 @@ public interface RedisConnectionDetails extends ConnectionDetails {
5051
}
5152

5253
/**
53-
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()} and
54-
* {@link #getCluster()}.
54+
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()},
55+
* {@link #getCluster()} and {@link #getStaticMasterReplica()}.
5556
* @return the Redis standalone configuration
5657
*/
5758
default @Nullable Standalone getStandalone() {
5859
return null;
5960
}
6061

6162
/**
62-
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()} and
63-
* {@link #getCluster()}.
63+
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()},
64+
* {@link #getCluster()} and {@link #getStaticMasterReplica()}.
6465
* @return the Redis sentinel configuration
6566
*/
6667
default @Nullable Sentinel getSentinel() {
6768
return null;
6869
}
6970

7071
/**
71-
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()} and
72-
* {@link #getSentinel()}.
72+
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()},
73+
* {@link #getSentinel()} and {@link #getStaticMasterReplica()}.
7374
* @return the Redis cluster configuration
7475
*/
7576
default @Nullable Cluster getCluster() {
7677
return null;
7778
}
7879

80+
/**
81+
* Redis static Master / Replica configuration. Mutually exclusive with {@link #getStandalone()},
82+
* {@link #getSentinel()} and {@link #getCluster()}.
83+
* @return the Redis static Master / Replica configuration
84+
*/
85+
default @Nullable StaticMasterReplica getStaticMasterReplica() {
86+
return null;
87+
}
88+
7989
/**
8090
* Redis standalone configuration.
8191
*/
@@ -245,7 +255,39 @@ interface Cluster {
245255
}
246256

247257
/**
248-
* A node in a sentinel or cluster configuration.
258+
* Redis static Master / Replica configuration.
259+
*/
260+
interface StaticMasterReplica {
261+
262+
/**
263+
* Database index used by the connection factory.
264+
* @return the database index used by the connection factory
265+
*/
266+
default int getDatabase() {
267+
return 0;
268+
}
269+
270+
/**
271+
* List of Master and Replica nodes for the static configuration. This represents
272+
* the nodes to be used in a static Master/Replica setup and is required to have
273+
* at least one entry. The first node does not need to be the master, as the actual
274+
* roles are determined by querying each node's ROLE command.
275+
* @return the list of nodes for Master/Replica configuration
276+
*/
277+
List<Node> getNodes();
278+
279+
/**
280+
* SSL bundle to use.
281+
* @return the SSL bundle to use
282+
*/
283+
default @Nullable SslBundle getSslBundle() {
284+
return null;
285+
}
286+
287+
}
288+
289+
/**
290+
* A node in a sentinel, cluster or static master-replica configuration.
249291
*
250292
* @param host the hostname of the node
251293
* @param port the port of the node

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/RedisProperties.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* @author Stephane Nicoll
3535
* @author Scott Frederick
3636
* @author Yanming Zhou
37+
* @author Yong-Hyun Kim
3738
* @since 4.0.0
3839
*/
3940
@ConfigurationProperties("spring.data.redis")
@@ -94,6 +95,8 @@ public class RedisProperties {
9495

9596
private @Nullable Cluster cluster;
9697

98+
private @Nullable StaticMasterReplica staticMasterReplica;
99+
97100
private final Ssl ssl = new Ssl();
98101

99102
private final Jedis jedis = new Jedis();
@@ -200,6 +203,14 @@ public void setCluster(@Nullable Cluster cluster) {
200203
this.cluster = cluster;
201204
}
202205

206+
public @Nullable StaticMasterReplica getStaticMasterReplica() {
207+
return this.staticMasterReplica;
208+
}
209+
210+
public void setStaticMasterReplica(@Nullable StaticMasterReplica staticMasterReplica) {
211+
this.staticMasterReplica = staticMasterReplica;
212+
}
213+
203214
public Jedis getJedis() {
204215
return this.jedis;
205216
}
@@ -413,6 +424,27 @@ public void setPassword(@Nullable String password) {
413424

414425
}
415426

427+
/**
428+
* Redis static master-replica properties.
429+
*/
430+
public static class StaticMasterReplica {
431+
432+
/**
433+
* List of "host:port" pairs regardless of role as the actual roles are determined
434+
* by querying each node's ROLE command.
435+
*/
436+
private @Nullable List<String> nodes;
437+
438+
public @Nullable List<String> getNodes() {
439+
return this.nodes;
440+
}
441+
442+
public void setNodes(@Nullable List<String> nodes) {
443+
this.nodes = nodes;
444+
}
445+
446+
}
447+
416448
public static class Ssl {
417449

418450
/**

0 commit comments

Comments
 (0)