Provide TLS support for Sentinel clients in Jedis driver.
Closes #1560 Original Pull Request: #2021
This commit is contained in:
committed by
Christoph Strobl
parent
c547ba2b6c
commit
4a2abdcd9b
@@ -15,15 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import redis.clients.jedis.BinaryJedis;
|
||||
import redis.clients.jedis.BinaryJedisPubSub;
|
||||
import redis.clients.jedis.Client;
|
||||
import redis.clients.jedis.Connection;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.MultiKeyPipelineBase;
|
||||
import redis.clients.jedis.Pipeline;
|
||||
import redis.clients.jedis.Response;
|
||||
import redis.clients.jedis.Transaction;
|
||||
import redis.clients.jedis.*;
|
||||
import redis.clients.jedis.exceptions.JedisDataException;
|
||||
import redis.clients.jedis.util.Pool;
|
||||
|
||||
@@ -49,7 +41,6 @@ import org.springframework.data.redis.connection.jedis.JedisResult.JedisStatusRe
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@code RedisConnection} implementation on top of <a href="https://github.com/xetorthio/jedis">Jedis</a> library.
|
||||
@@ -81,6 +72,8 @@ public class JedisConnection extends AbstractRedisConnection {
|
||||
|
||||
private final @Nullable Pool<Jedis> pool;
|
||||
private final String clientName;
|
||||
private final JedisClientConfig nodeConfig;
|
||||
private final JedisClientConfig sentinelConfig;
|
||||
|
||||
private List<JedisResult> pipelinedResults = new ArrayList<>();
|
||||
private Queue<FutureResult<Response<?>>> txResults = new LinkedList<>();
|
||||
@@ -120,18 +113,38 @@ public class JedisConnection extends AbstractRedisConnection {
|
||||
* @param clientName the client name, can be {@literal null}.
|
||||
* @since 1.8
|
||||
*/
|
||||
protected JedisConnection(Jedis jedis, @Nullable Pool<Jedis> pool, int dbIndex, String clientName) {
|
||||
protected JedisConnection(Jedis jedis, @Nullable Pool<Jedis> pool, int dbIndex, @Nullable String clientName) {
|
||||
this(jedis, pool, createConfig(dbIndex, clientName), createConfig(dbIndex, clientName));
|
||||
}
|
||||
|
||||
private static DefaultJedisClientConfig createConfig(int dbIndex, @Nullable String clientName) {
|
||||
return DefaultJedisClientConfig.builder().databse(dbIndex).clientName(clientName).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>JedisConnection</code> instance backed by a jedis pool.
|
||||
*
|
||||
* @param jedis
|
||||
* @param pool can be null, if no pool is used
|
||||
* @param nodeConfig node configuration
|
||||
* @param sentinelConfig sentinel configuration
|
||||
* @since 2.5
|
||||
*/
|
||||
protected JedisConnection(Jedis jedis, @Nullable Pool<Jedis> pool, JedisClientConfig nodeConfig,
|
||||
JedisClientConfig sentinelConfig) {
|
||||
|
||||
this.jedis = jedis;
|
||||
this.pool = pool;
|
||||
this.clientName = clientName;
|
||||
this.clientName = nodeConfig.getClientName();
|
||||
this.nodeConfig = nodeConfig;
|
||||
this.sentinelConfig = sentinelConfig;
|
||||
|
||||
// select the db
|
||||
// if this fail, do manual clean-up before propagating the exception
|
||||
// as we're inside the constructor
|
||||
if (dbIndex != jedis.getDB()) {
|
||||
if (nodeConfig.getDatabase() != jedis.getDB()) {
|
||||
try {
|
||||
select(dbIndex);
|
||||
select(nodeConfig.getDatabase());
|
||||
} catch (DataAccessException ex) {
|
||||
close();
|
||||
throw ex;
|
||||
@@ -776,14 +789,7 @@ public class JedisConnection extends AbstractRedisConnection {
|
||||
}
|
||||
|
||||
protected Jedis getJedis(RedisNode node) {
|
||||
|
||||
Jedis jedis = new Jedis(node.getHost(), node.getPort());
|
||||
|
||||
if (StringUtils.hasText(clientName)) {
|
||||
jedis.clientSetname(clientName);
|
||||
}
|
||||
|
||||
return jedis;
|
||||
return new Jedis(new HostAndPort(node.getHost(), node.getPort()), this.sentinelConfig);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import redis.clients.jedis.Client;
|
||||
import redis.clients.jedis.DefaultJedisClientConfig;
|
||||
import redis.clients.jedis.HostAndPort;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisClientConfig;
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
@@ -41,6 +42,7 @@ import javax.net.ssl.SSLSocketFactory;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@@ -58,6 +60,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -88,6 +91,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
|
||||
private final JedisClientConfiguration clientConfiguration;
|
||||
private @Nullable JedisShardInfo shardInfo;
|
||||
private JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().build();
|
||||
private boolean providedShardInfo = false;
|
||||
private @Nullable Pool<Jedis> pool;
|
||||
private boolean convertPipelineAndTxResults = true;
|
||||
@@ -290,17 +294,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
return new Jedis(getShardInfo());
|
||||
}
|
||||
|
||||
Jedis jedis = new Jedis(getHostName(), getPort(), getConnectTimeout(), getReadTimeout(), isUseSsl(),
|
||||
clientConfiguration.getSslSocketFactory().orElse(null), //
|
||||
clientConfiguration.getSslParameters().orElse(null), //
|
||||
clientConfiguration.getHostnameVerifier().orElse(null));
|
||||
|
||||
Client client = jedis.getClient();
|
||||
|
||||
getRedisPassword().map(String::new).ifPresent(client::setPassword);
|
||||
client.setDb(getDatabase());
|
||||
|
||||
return jedis;
|
||||
return new Jedis(new HostAndPort(getHostName(), getPort()), this.clientConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,6 +314,8 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
clientConfig = createClientConfig(getRedisUsername(), getRedisPassword());
|
||||
|
||||
if (shardInfo == null && clientConfiguration instanceof MutableJedisClientConfiguration) {
|
||||
|
||||
providedShardInfo = false;
|
||||
@@ -357,6 +353,33 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
}
|
||||
}
|
||||
|
||||
private JedisClientConfig createClientConfig(@Nullable String username, RedisPassword password) {
|
||||
|
||||
DefaultJedisClientConfig.Builder builder = DefaultJedisClientConfig.builder();
|
||||
|
||||
clientConfiguration.getClientName().ifPresent(builder::clientName);
|
||||
builder.connectionTimeoutMillis(getConnectTimeout());
|
||||
builder.socketTimeoutMillis(getReadTimeout());
|
||||
|
||||
builder.databse(getDatabase());
|
||||
|
||||
if (!ObjectUtils.isEmpty(username)) {
|
||||
builder.user(username);
|
||||
}
|
||||
password.toOptional().map(String::new).ifPresent(builder::password);
|
||||
|
||||
if (isUseSsl()) {
|
||||
|
||||
builder.ssl(true);
|
||||
|
||||
clientConfiguration.getSslSocketFactory().ifPresent(builder::sslSocketFactory);
|
||||
clientConfiguration.getHostnameVerifier().ifPresent(builder::hostnameVerifier);
|
||||
clientConfiguration.getSslParameters().ifPresent(builder::sslParameters);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Pool<Jedis> createPool() {
|
||||
|
||||
if (isRedisSentinelAware()) {
|
||||
@@ -374,13 +397,12 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
*/
|
||||
protected Pool<Jedis> createRedisSentinelPool(RedisSentinelConfiguration config) {
|
||||
|
||||
GenericObjectPoolConfig<?> poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
|
||||
GenericObjectPoolConfig<Jedis> poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
|
||||
String sentinelUser = null;
|
||||
String sentinelPassword = config.getSentinelPassword().toOptional().map(String::new).orElse(null);
|
||||
|
||||
JedisClientConfig sentinelConfig = createClientConfig(sentinelUser, config.getSentinelPassword());
|
||||
return new JedisSentinelPool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()),
|
||||
poolConfig, getConnectTimeout(), getReadTimeout(), getUsername(), getPassword(), getDatabase(), getClientName(),
|
||||
getConnectTimeout(), getReadTimeout(), sentinelUser, sentinelPassword, getClientName());
|
||||
poolConfig, this.clientConfig, sentinelConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,12 +412,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
* @since 1.4
|
||||
*/
|
||||
protected Pool<Jedis> createRedisPool() {
|
||||
|
||||
return new JedisPool(getPoolConfig(), getHostName(), getPort(), getConnectTimeout(), getReadTimeout(),
|
||||
getUsername(), getPassword(), getDatabase(), getClientName(), isUseSsl(),
|
||||
clientConfiguration.getSslSocketFactory().orElse(null), //
|
||||
clientConfiguration.getSslParameters().orElse(null), //
|
||||
clientConfiguration.getHostnameVerifier().orElse(null));
|
||||
return new JedisPool(getPoolConfig(), new HostAndPort(getHostName(), getPort()), this.clientConfig);
|
||||
}
|
||||
|
||||
private JedisCluster createCluster() {
|
||||
@@ -423,7 +440,8 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
* @return the actual {@link JedisCluster}.
|
||||
* @since 1.7
|
||||
*/
|
||||
protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, GenericObjectPoolConfig<?> poolConfig) {
|
||||
protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig,
|
||||
GenericObjectPoolConfig<Jedis> poolConfig) {
|
||||
|
||||
Assert.notNull(clusterConfig, "Cluster configuration must not be null!");
|
||||
|
||||
@@ -434,10 +452,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
|
||||
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects() : 5;
|
||||
|
||||
return new JedisCluster(hostAndPort, getConnectTimeout(), getReadTimeout(), redirects, getUsername(), getPassword(),
|
||||
getClientName(), poolConfig, isUseSsl(), clientConfiguration.getSslSocketFactory().orElse(null),
|
||||
clientConfiguration.getSslParameters().orElse(null), clientConfiguration.getHostnameVerifier().orElse(null),
|
||||
null);
|
||||
return new JedisCluster(hostAndPort, this.clientConfig, redirects, poolConfig);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -553,16 +568,6 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
getMutableConfiguration().setUseSsl(useSsl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username used for authenticating with the Redis server.
|
||||
*
|
||||
* @return username for authentication.
|
||||
*/
|
||||
@Nullable
|
||||
private String getUsername() {
|
||||
return getRedisUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password used for authenticating with the Redis server.
|
||||
*
|
||||
@@ -715,7 +720,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
* @return the poolConfig
|
||||
*/
|
||||
@Nullable
|
||||
public GenericObjectPoolConfig getPoolConfig() {
|
||||
public GenericObjectPoolConfig<Jedis> getPoolConfig() {
|
||||
return clientConfiguration.getPoolConfig().orElse(null);
|
||||
}
|
||||
|
||||
@@ -877,35 +882,41 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
Assert.isTrue(RedisConfiguration.isSentinelConfiguration(configuration), "SentinelConfig must not be null!");
|
||||
SentinelConfiguration sentinelConfiguration = (SentinelConfiguration) configuration;
|
||||
|
||||
JedisClientConfig clientConfig = createClientConfig(null, sentinelConfiguration.getSentinelPassword());
|
||||
for (RedisNode node : sentinelConfiguration.getSentinels()) {
|
||||
|
||||
Jedis jedis = new Jedis(node.getHost(), node.getPort(), getConnectTimeout(), getReadTimeout());
|
||||
sentinelConfiguration.getSentinelPassword().toOptional().map(String::new).ifPresent(jedis::auth);
|
||||
Jedis jedis = null;
|
||||
boolean success = false;
|
||||
|
||||
try {
|
||||
if (jedis.ping().equalsIgnoreCase("pong")) {
|
||||
|
||||
potentiallySetClientName(jedis);
|
||||
jedis = new Jedis(new HostAndPort(node.getHost(), node.getPort()), clientConfig);
|
||||
if (jedis.ping().equalsIgnoreCase("pong")) {
|
||||
success = true;
|
||||
return jedis;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.warn(String.format("Ping failed for sentinel host:%s", node.getHost()), ex);
|
||||
} finally {
|
||||
if (!success && jedis != null) {
|
||||
jedis.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidDataAccessResourceUsageException("No Sentinel found");
|
||||
}
|
||||
|
||||
private Set<String> convertToJedisSentinelSet(Collection<RedisNode> nodes) {
|
||||
private static Set<HostAndPort> convertToJedisSentinelSet(Collection<RedisNode> nodes) {
|
||||
|
||||
if (CollectionUtils.isEmpty(nodes)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<String> convertedNodes = new LinkedHashSet<>(nodes.size());
|
||||
Set<HostAndPort> convertedNodes = new LinkedHashSet<>(nodes.size());
|
||||
for (RedisNode node : nodes) {
|
||||
if (node != null) {
|
||||
convertedNodes.add(node.asString());
|
||||
convertedNodes.add(new HostAndPort(node.getHost(), node.getPort()));
|
||||
}
|
||||
}
|
||||
return convertedNodes;
|
||||
|
||||
Reference in New Issue
Block a user