DATAREDIS-315 - Support password protected Redis Cluster.

Add new config property to configure a password for operations with Redis Cluster. The only driver to support Redis Cluster with password is lettuce but 3.4.Final has a blocker bug that prevents the usage

Original pull request: #158.
This commit is contained in:
Mark Paluch
2016-02-04 15:32:57 +01:00
parent f18def013a
commit 0ac118fcc0
5 changed files with 80 additions and 6 deletions

View File

@@ -56,6 +56,17 @@ public class AppConfig {
----
====
[TIP]
====
`RedisClusterConfiguration` can also be defined via `PropertySource`.
.Configuration Properties
- `spring.redis.cluster.nodes`: Comma delimited list of host:port pairs.
- `spring.redis.cluster.timeout`: Timeout (in milliseconds) for cluster operations.
- `spring.redis.cluster.max-redirects`: Number of allowed cluster redirections.
- `spring.redis.cluster.password`: Password to access a password-protected Redis Cluster. Must equal across all nodes.
====
NOTE: The initial configuration points driver libraries to an initial set of cluster nodes. Changes resulting from live cluster reconfiguration will only be kept in the native driver and not be written back to the configuration.
== Working With Redis Cluster Connection

View File

@@ -27,6 +27,7 @@ import java.util.Set;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
@@ -36,6 +37,7 @@ import org.springframework.util.StringUtils;
* environment.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.7
*/
public class RedisClusterConfiguration {
@@ -43,10 +45,12 @@ public class RedisClusterConfiguration {
private static final String REDIS_CLUSTER_NODES_CONFIG_PROPERTY = "spring.redis.cluster.nodes";
private static final String REDIS_CLUSTER_TIMEOUT_CONFIG_PROPERTY = "spring.redis.cluster.timeout";
private static final String REDIS_CLUSTER_MAX_REDIRECTS_CONFIG_PROPERTY = "spring.redis.cluster.max-redirects";
private static final String REDIS_CLUSTER_PASSWORD_PROPERTY = "spring.redis.cluster.password";
private Set<RedisNode> clusterNodes;
private Long clusterTimeout;
private Integer maxRedirects;
private String password;
/**
* Creates new {@link RedisClusterConfiguration}.
@@ -69,7 +73,7 @@ public class RedisClusterConfiguration {
* {@literal null}.
*/
public RedisClusterConfiguration(Collection<String> clusterNodes) {
this(new MapPropertySource("RedisClusterConfiguration", asMap(clusterNodes, -1, -1)));
this(new MapPropertySource("RedisClusterConfiguration", asMap(clusterNodes, -1, -1, null)));
}
/**
@@ -80,6 +84,7 @@ public class RedisClusterConfiguration {
* spring.redis.cluster.nodes=127.0.0.1:23679,127.0.0.1:23680,127.0.0.1:23681
* spring.redis.cluster.timeout=5
* spring.redis.cluster.max-redirects=3
* spring.redis.cluster.password=foobar
* </code>
* </pre>
*
@@ -103,6 +108,9 @@ public class RedisClusterConfiguration {
this.maxRedirects = NumberUtils.parseNumber(
propertySource.getProperty(REDIS_CLUSTER_MAX_REDIRECTS_CONFIG_PROPERTY).toString(), Integer.class);
}
if (propertySource.containsProperty(REDIS_CLUSTER_PASSWORD_PROPERTY)) {
this.password = propertySource.getProperty(REDIS_CLUSTER_PASSWORD_PROPERTY).toString();
}
}
/**
@@ -156,6 +164,14 @@ public class RedisClusterConfiguration {
return clusterTimeout != null && clusterTimeout > Long.MIN_VALUE ? clusterTimeout : null;
}
/**
*
* @param clusterTimeout
*/
public void setClusterTimeout(long clusterTimeout) {
this.clusterTimeout = clusterTimeout;
}
/**
* @return
*/
@@ -163,6 +179,30 @@ public class RedisClusterConfiguration {
return maxRedirects != null && maxRedirects > Integer.MIN_VALUE ? maxRedirects : null;
}
/**
*
* @param maxRedirects
*/
public void setMaxRedirects(int maxRedirects) {
Assert.isTrue(maxRedirects >= 0, "MaxRedirects must be greater or equal to 0");
this.maxRedirects = maxRedirects;
}
/**
* @return
*/
public String getPassword() {
return password;
}
/**
*
* @param password can be {@literal null} or empty.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @param host
* @param port
@@ -189,11 +229,13 @@ public class RedisClusterConfiguration {
}
/**
* @param master must not be {@literal null} or empty.
* @param clusterHostAndPorts must not be {@literal null}.
* @param clusterHostAndPorts must not be {@literal null} or empty.
* @param timeout
* @param redirects
* @param password can be {@literal null} or empty.
* @return
*/
private static Map<String, Object> asMap(Collection<String> clusterHostAndPorts, long timeout, int redirects) {
private static Map<String, Object> asMap(Collection<String> clusterHostAndPorts, long timeout, int redirects, String password) {
notNull(clusterHostAndPorts, "ClusterHostAndPorts must not be null!");
@@ -205,6 +247,9 @@ public class RedisClusterConfiguration {
if (redirects >= 0) {
map.put(REDIS_CLUSTER_MAX_REDIRECTS_CONFIG_PROPERTY, Integer.valueOf(redirects));
}
if (StringUtils.hasText(password)) {
map.put(REDIS_CLUSTER_PASSWORD_PROPERTY, password);
}
return map;
}

View File

@@ -62,6 +62,7 @@ import redis.clients.util.Pool;
* @author Costin Leau
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
@@ -292,8 +293,12 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
hostAndPort.add(new HostAndPort(node.getHost(), node.getPort()));
}
int timeout = clusterConfig.getClusterTimeout() != null ? clusterConfig.getClusterTimeout().intValue() : 1;
int timeout = clusterConfig.getClusterTimeout() != null ? clusterConfig.getClusterTimeout().intValue() : this.timeout;
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects().intValue() : 5;
if (StringUtils.hasText(clusterConfig.getPassword())) {
throw new UnsupportedOperationException("Jedis does not support password protected Redis Cluster configurations");
}
if (poolConfig != null) {
return new JedisCluster(hostAndPort, timeout, redirects, poolConfig);

View File

@@ -49,6 +49,7 @@ import com.lambdaworks.redis.RedisException;
import com.lambdaworks.redis.RedisFuture;
import com.lambdaworks.redis.RedisURI;
import com.lambdaworks.redis.cluster.RedisClusterClient;
import org.springframework.util.StringUtils;
/**
* Connection factory creating <a href="http://github.com/mp911de/lettuce">Lettuce</a>-based connections.
@@ -464,7 +465,16 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
List<RedisURI> initialUris = new ArrayList<RedisURI>();
for (RedisNode node : this.clusterConfiguration.getClusterNodes()) {
initialUris.add(new RedisURI(node.getHost(), node.getPort(), this.timeout, TimeUnit.MILLISECONDS));
long timeout = this.clusterConfiguration.getClusterTimeout() != null ? this.clusterConfiguration.getClusterTimeout() : this.timeout;
RedisURI redisURI = new RedisURI(node.getHost(), node.getPort(), timeout, TimeUnit.MILLISECONDS);
if(StringUtils.hasText(this.clusterConfiguration.getPassword())){
redisURI.setPassword(this.clusterConfiguration.getPassword());
}else if(StringUtils.hasText(password)){
redisURI.setPassword(password);
}
initialUris.add(redisURI);
}
RedisClusterClient clusterClient = new RedisClusterClient(initialUris);

View File

@@ -31,6 +31,7 @@ import org.springframework.util.StringUtils;
/**
* @author Christoph Strobl
* @author Mark Paluch
*/
public class RedisClusterConfigurationUnitTests {
@@ -125,12 +126,14 @@ public class RedisClusterConfigurationUnitTests {
propertySource.setProperty("spring.redis.cluster.timeout", "10");
propertySource.setProperty("spring.redis.cluster.nodes", HOST_AND_PORT_1);
propertySource.setProperty("spring.redis.cluster.max-redirects", "5");
propertySource.setProperty("spring.redis.cluster.password", "foobar");
RedisClusterConfiguration config = new RedisClusterConfiguration(propertySource);
assertThat(config.getMaxRedirects(), is(5));
assertThat(config.getClusterTimeout(), is(10L));
assertThat(config.getClusterNodes(), hasItems(new RedisNode("127.0.0.1", 123)));
assertThat(config.getPassword(), is("foobar"));
}
/**