Skip to content

Commit 0aa99dd

Browse files
committed
Support Sentinel with TLS
1 parent 6f9fb61 commit 0aa99dd

File tree

4 files changed

+155
-46
lines changed

4 files changed

+155
-46
lines changed

Diff for: Makefile

+18
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ pid = /tmp/stunnel.pid
254254
[redis]
255255
accept = 127.0.0.1:6390
256256
connect = 127.0.0.1:6379
257+
[redis_3]
258+
accept = 127.0.0.1:16381
259+
connect = 127.0.0.1:6381
260+
[redis_4]
261+
accept = 127.0.0.1:16382
262+
connect = 127.0.0.1:6382
257263
[redis_cluster_1]
258264
accept = 127.0.0.1:8379
259265
connect = 127.0.0.1:7379
@@ -269,6 +275,18 @@ connect = 127.0.0.1:7382
269275
[redis_cluster_5]
270276
accept = 127.0.0.1:8383
271277
connect = 127.0.0.1:7383
278+
[redis_sentinel_1]
279+
accept = 127.0.0.1:36379
280+
connect = 127.0.0.1:26379
281+
[redis_sentinel_2]
282+
accept = 127.0.0.1:36380
283+
connect = 127.0.0.1:26380
284+
[redis_sentinel_3]
285+
accept = 127.0.0.1:36381
286+
connect = 127.0.0.1:26381
287+
[redis_sentinel_4]
288+
accept = 127.0.0.1:36382
289+
connect = 127.0.0.1:26382
272290
endef
273291

274292
export REDIS1_CONF

Diff for: src/main/java/redis/clients/jedis/JedisFactory.java

+16-9
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class JedisFactory implements PooledObjectFactory<Jedis> {
2626

2727
private final AtomicReference<HostAndPort> hostAndPort = new AtomicReference<>();
2828

29-
private final JedisClientConfig config;
29+
private final JedisClientConfig clientConfig;
3030

3131
protected JedisFactory(final String host, final int port, final int connectionTimeout,
3232
final int soTimeout, final String password, final int database, final String clientName) {
@@ -67,7 +67,7 @@ protected JedisFactory(final String host, final int port, final int connectionTi
6767

6868
protected JedisFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) {
6969
this.hostAndPort.set(hostAndPort);
70-
this.config = DefaultJedisClientConfig.copyConfig(clientConfig);
70+
this.clientConfig = DefaultJedisClientConfig.copyConfig(clientConfig);
7171
}
7272

7373
protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout,
@@ -84,11 +84,18 @@ protected JedisFactory(final String host, final int port, final int connectionTi
8484
protected JedisFactory(final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout,
8585
final String user, final String password, final int database, final String clientName, final boolean ssl,
8686
final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
87-
this.config = DefaultJedisClientConfig.builder().withConnectionTimeoutMillis(connectionTimeout)
87+
this(DefaultJedisClientConfig.builder().withConnectionTimeoutMillis(connectionTimeout)
8888
.withSoTimeoutMillis(soTimeout).withInfiniteSoTimeoutMillis(infiniteSoTimeout).withUser(user)
8989
.withPassword(password).withDatabse(database).withClientName(clientName)
9090
.withSsl(ssl).withSslSocketFactory(sslSocketFactory)
91-
.withSslParameters(sslParameters).withHostnameVerifier(hostnameVerifier).build();
91+
.withSslParameters(sslParameters).withHostnameVerifier(hostnameVerifier).build());
92+
}
93+
94+
/**
95+
* {@link #setHostAndPort(redis.clients.jedis.HostAndPort) setHostAndPort} must be called later.
96+
*/
97+
protected JedisFactory(final JedisClientConfig clientConfig) {
98+
this.clientConfig = clientConfig;
9299
}
93100

94101
protected JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout,
@@ -110,7 +117,7 @@ protected JedisFactory(final URI uri, final int connectionTimeout, final int soT
110117
"Cannot open Redis connection due invalid URI. %s", uri.toString()));
111118
}
112119
this.hostAndPort.set(new HostAndPort(uri.getHost(), uri.getPort()));
113-
this.config = DefaultJedisClientConfig.builder().withConnectionTimeoutMillis(connectionTimeout)
120+
this.clientConfig = DefaultJedisClientConfig.builder().withConnectionTimeoutMillis(connectionTimeout)
114121
.withSoTimeoutMillis(soTimeout).withInfiniteSoTimeoutMillis(infiniteSoTimeout)
115122
.withUser(JedisURIHelper.getUser(uri)).withPassword(JedisURIHelper.getPassword(uri))
116123
.withDatabse(JedisURIHelper.getDBIndex(uri)).withClientName(clientName)
@@ -123,14 +130,14 @@ public void setHostAndPort(final HostAndPort hostAndPort) {
123130
}
124131

125132
public void setPassword(final String password) {
126-
this.config.updatePassword(password);
133+
this.clientConfig.updatePassword(password);
127134
}
128135

129136
@Override
130137
public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception {
131138
final BinaryJedis jedis = pooledJedis.getObject();
132-
if (jedis.getDB() != config.getDatabase()) {
133-
jedis.select(config.getDatabase());
139+
if (jedis.getDB() != clientConfig.getDatabase()) {
140+
jedis.select(clientConfig.getDatabase());
134141
}
135142
}
136143

@@ -159,7 +166,7 @@ public PooledObject<Jedis> makeObject() throws Exception {
159166
final HostAndPort hostPort = this.hostAndPort.get();
160167
Jedis jedis = null;
161168
try {
162-
jedis = new Jedis(hostPort, config);
169+
jedis = new Jedis(hostPort, clientConfig);
163170
jedis.connect();
164171
return new DefaultPooledObject<>(jedis);
165172
} catch (JedisException je) {

Diff for: src/main/java/redis/clients/jedis/JedisSentinelPool.java

+45-37
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66
import java.util.Set;
77
import java.util.concurrent.atomic.AtomicBoolean;
8+
import java.util.stream.Collectors;
89

910
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
1011
import org.slf4j.Logger;
@@ -21,7 +22,7 @@ public class JedisSentinelPool extends JedisPoolAbstract {
2122
@Deprecated
2223
protected static Logger log = LoggerFactory.getLogger(JedisSentinelPool.class);
2324

24-
protected final GenericObjectPoolConfig<Jedis> poolConfig;
25+
@Deprecated protected final GenericObjectPoolConfig<Jedis> poolConfig;
2526
private final JedisFactory factory;
2627

2728
@Deprecated protected int connectionTimeout;
@@ -39,6 +40,8 @@ public class JedisSentinelPool extends JedisPoolAbstract {
3940
@Deprecated protected String sentinelPassword;
4041
@Deprecated protected String sentinelClientName;
4142

43+
private final JedisClientConfig sentinelClientConfig;
44+
4245
protected final Set<MasterListener> masterListeners = new HashSet<>();
4346

4447
private volatile HostAndPort currentHostMaster;
@@ -177,14 +180,33 @@ public JedisSentinelPool(String masterName, Set<String> sentinels,
177180

178181
public JedisSentinelPool(String masterName, Set<String> sentinels,
179182
final GenericObjectPoolConfig<Jedis> poolConfig, final JedisFactory factory) {
183+
this(masterName, parseHostAndPorts(sentinels), poolConfig, factory,
184+
DefaultJedisClientConfig.builder().build());
185+
}
186+
187+
public JedisSentinelPool(String masterName, Set<HostAndPort> sentinels,
188+
final GenericObjectPoolConfig<Jedis> poolConfig, final JedisClientConfig masteClientConfig,
189+
final JedisClientConfig sentinelClientConfig) {
190+
this(masterName, sentinels, poolConfig, new JedisFactory(masteClientConfig), sentinelClientConfig);
191+
}
192+
193+
public JedisSentinelPool(String masterName, Set<HostAndPort> sentinels,
194+
final GenericObjectPoolConfig<Jedis> poolConfig, final JedisFactory factory,
195+
final JedisClientConfig sentinelClientConfig) {
180196
super(poolConfig, factory);
197+
181198
this.poolConfig = poolConfig;
182199
this.factory = factory;
200+
this.sentinelClientConfig = sentinelClientConfig;
183201

184202
HostAndPort master = initSentinels(sentinels, masterName);
185203
initMaster(master);
186204
}
187205

206+
private static Set<HostAndPort> parseHostAndPorts(Set<String> strings) {
207+
return strings.parallelStream().map(str -> HostAndPort.parseString(str)).collect(Collectors.toSet());
208+
}
209+
188210
@Override
189211
public void destroy() {
190212
for (MasterListener m : masterListeners) {
@@ -212,51 +234,44 @@ private void initMaster(HostAndPort master) {
212234
}
213235
}
214236

215-
private HostAndPort initSentinels(Set<String> sentinels, final String masterName) {
237+
private HostAndPort initSentinels(Set<HostAndPort> sentinels, final String masterName) {
216238

217239
HostAndPort master = null;
218240
boolean sentinelAvailable = false;
219241

220242
log.info("Trying to find master from available Sentinels...");
221243

222-
for (String sentinel : sentinels) {
223-
final HostAndPort hap = HostAndPort.parseString(sentinel);
244+
for (HostAndPort sentinel : sentinels) {
224245

225-
log.debug("Connecting to Sentinel {}", hap);
246+
log.debug("Connecting to Sentinel {}", sentinel);
226247

227-
try (Jedis jedis = new Jedis(hap.getHost(), hap.getPort(), sentinelConnectionTimeout, sentinelSoTimeout)) {
228-
if (sentinelUser != null) {
229-
jedis.auth(sentinelUser, sentinelPassword);
230-
} else if (sentinelPassword != null) {
231-
jedis.auth(sentinelPassword);
232-
}
233-
if (sentinelClientName != null) {
234-
jedis.clientSetname(sentinelClientName);
235-
}
248+
try (Jedis jedis = new Jedis(sentinel, sentinelClientConfig)) {
236249

237250
List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName);
238251

239252
// connected to sentinel...
240253
sentinelAvailable = true;
241254

242255
if (masterAddr == null || masterAddr.size() != 2) {
243-
log.warn("Can not get master addr, master name: {}. Sentinel: {}", masterName, hap);
256+
log.warn("Can not get master addr, master name: {}. Sentinel: {}", masterName, sentinel);
244257
continue;
245258
}
246259

247260
master = toHostAndPort(masterAddr);
248261
log.debug("Found Redis master at {}", master);
249262
break;
250263
} catch (JedisException e) {
251-
// resolves #1036, it should handle JedisException there's another chance of raising JedisDataException
252-
log.warn("Cannot get master address from sentinel running @ {}. Reason: {}. Trying next one.", hap, e);
264+
// resolves #1036, it should handle JedisException there's another chance
265+
// of raising JedisDataException
266+
log.warn(
267+
"Cannot get master address from sentinel running @ {}. Reason: {}. Trying next one.",
268+
sentinel, e);
253269
}
254270
}
255271

256272
if (master == null) {
257273
if (sentinelAvailable) {
258-
// can connect to sentinel, but master name seems to not
259-
// monitored
274+
// can connect to sentinel, but master name seems to not monitored
260275
throw new JedisException("Can connect to sentinel, but " + masterName
261276
+ " seems to be not monitored...");
262277
} else {
@@ -267,9 +282,9 @@ private HostAndPort initSentinels(Set<String> sentinels, final String masterName
267282

268283
log.info("Redis master running at {}, starting Sentinel listeners...", master);
269284

270-
for (String sentinel : sentinels) {
271-
final HostAndPort hap = HostAndPort.parseString(sentinel);
272-
MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
285+
for (HostAndPort sentinel : sentinels) {
286+
287+
MasterListener masterListener = new MasterListener(masterName, sentinel.getHost(), sentinel.getPort());
273288
// whether MasterListener threads are alive or not, process can be stopped
274289
masterListener.setDaemon(true);
275290
masterListeners.add(masterListener);
@@ -357,28 +372,22 @@ public void run() {
357372
break;
358373
}
359374

360-
j = new Jedis(host, port, sentinelConnectionTimeout, sentinelSoTimeout);
361-
if (sentinelUser != null) {
362-
j.auth(sentinelUser, sentinelPassword);
363-
} else if (sentinelPassword != null) {
364-
j.auth(sentinelPassword);
365-
}
366-
if (sentinelClientName != null) {
367-
j.clientSetname(sentinelClientName);
368-
}
375+
final HostAndPort hostPort = new HostAndPort(host, port);
376+
j = new Jedis(hostPort, sentinelClientConfig);
369377

370378
// code for active refresh
371379
List<String> masterAddr = j.sentinelGetMasterAddrByName(masterName);
372380
if (masterAddr == null || masterAddr.size() != 2) {
373-
log.warn("Can not get master addr, master name: {}. Sentinel: {}:{}.", masterName, host, port);
381+
log.warn("Can not get master addr, master name: {}. Sentinel: {}.", masterName,
382+
hostPort);
374383
} else {
375384
initMaster(toHostAndPort(masterAddr));
376385
}
377386

378387
j.subscribe(new JedisPubSub() {
379388
@Override
380389
public void onMessage(String channel, String message) {
381-
log.debug("Sentinel {}:{} published: {}.", host, port, message);
390+
log.debug("Sentinel {} published: {}.", hostPort, message);
382391

383392
String[] switchMasterMsg = message.split(" ");
384393

@@ -393,9 +402,8 @@ public void onMessage(String channel, String message) {
393402
}
394403

395404
} else {
396-
log.error(
397-
"Invalid message received on Sentinel {}:{} on channel +switch-master: {}", host,
398-
port, message);
405+
log.error("Invalid message received on Sentinel {} on channel +switch-master: {}",
406+
hostPort, message);
399407
}
400408
}
401409
}, "+switch-master");
@@ -427,7 +435,7 @@ public void shutdown() {
427435
running.set(false);
428436
// This isn't good, the Jedis object is not thread safe
429437
if (j != null) {
430-
j.disconnect();
438+
j.close();
431439
}
432440
} catch (Exception e) {
433441
log.error("Caught exception while shutting down: ", e);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package redis.clients.jedis.tests;
2+
3+
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
4+
import org.junit.BeforeClass;
5+
import org.junit.Test;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
9+
import redis.clients.jedis.DefaultJedisClientConfig;
10+
import redis.clients.jedis.HostAndPort;
11+
import redis.clients.jedis.HostAndPortMapper;
12+
import redis.clients.jedis.Jedis;
13+
import redis.clients.jedis.JedisSentinelPool;
14+
15+
public class SSLJedisSentinelPoolTest {
16+
17+
private static final String MASTER_NAME = "mymaster";
18+
19+
private static Set<HostAndPort> sentinels = new HashSet<>();
20+
21+
private static final HostAndPortMapper SSL_PORT_MAPPER = (HostAndPort hap)
22+
-> new HostAndPort(hap.getHost(), hap.getPort() + 10000);
23+
24+
private static final GenericObjectPoolConfig<Jedis> POOL_CONFIG = new GenericObjectPoolConfig<>();
25+
26+
@BeforeClass
27+
public static void prepare() {
28+
SSLJedisTest.setupTrustStore();
29+
30+
sentinels.add(HostAndPortUtil.getSentinelServers().get(1));
31+
sentinels.add(HostAndPortUtil.getSentinelServers().get(3));
32+
}
33+
34+
@Test
35+
public void sentinelWithoutSslConnectsToRedisWithSsl() {
36+
DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder()
37+
.withPassword("foobared").withClientName("sentinel-master-client")
38+
.withSsl(true).withHostAndPortMapper(SSL_PORT_MAPPER).build();
39+
DefaultJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder()
40+
.withClientName("sentinel-client")
41+
.withSsl(false).build();
42+
try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
43+
masterConfig, sentinelConfig)) {
44+
pool.getResource().close();
45+
}
46+
}
47+
48+
@Test
49+
public void sentinelWithSslConnectsToRedisWithoutSsl() {
50+
DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder()
51+
.withPassword("foobared").withClientName("sentinel-master-client")
52+
.withSsl(false).build();
53+
DefaultJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder()
54+
.withClientName("sentinel-client")
55+
.withSsl(true).withHostAndPortMapper(SSL_PORT_MAPPER).build();
56+
try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
57+
masterConfig, sentinelConfig)) {
58+
pool.getResource().close();
59+
}
60+
}
61+
62+
@Test
63+
public void sentinelWithSslConnectsToRedisWithSsl() {
64+
DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder()
65+
.withPassword("foobared").withClientName("sentinel-master-client")
66+
.withSsl(true).withHostAndPortMapper(SSL_PORT_MAPPER).build();
67+
DefaultJedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder()
68+
.withClientName("sentinel-client")
69+
.withSsl(true).withHostAndPortMapper(SSL_PORT_MAPPER).build();
70+
try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
71+
masterConfig, sentinelConfig)) {
72+
pool.getResource().close();
73+
}
74+
}
75+
76+
}

0 commit comments

Comments
 (0)