mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Add support for Redis Sentinel configuration
Spring Data Redis 1.4.0 introduced Redis Sentinel support. When specified, RedisConnectionFactory uses the Sentinel configuration to determine the current master. Sentinel configuration can be specified using two new properties: spring.redis.sentinel.master and spring.redis.sentinel.nodes. For example: spring.redis.sentinel.master=mymaster # name of redis server spring.redis.sentinel.nodes=127.0.0.1:26379,127.0.0.1:26380 Alternatively, a bean of type RedisSentinelConfiguration can be declared and it will be used to configure the connection factory. Note: At this time, Sentinel support is only available for Jedis Closes gh-1337
This commit is contained in:
parent
fbeb8c966c
commit
c8a4891441
@ -17,6 +17,8 @@
|
||||
package org.springframework.boot.autoconfigure.redis;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -24,15 +26,19 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.redis.RedisProperties.Sentinel;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnection;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
@ -43,6 +49,7 @@ import redis.clients.jedis.JedisPoolConfig;
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Christian Dupuis
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
|
||||
@ -55,17 +62,58 @@ public class RedisAutoConfiguration {
|
||||
return new RedisProperties();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass(name = "org.apache.commons.pool2.impl.GenericObjectPool")
|
||||
protected static class RedisConnectionConfiguration {
|
||||
protected abstract static class RedisHAConnectionConfiguration {
|
||||
|
||||
@Autowired
|
||||
private RedisProperties properties;
|
||||
protected RedisProperties properties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RedisSentinelConfiguration sentinelConfiguration;
|
||||
|
||||
protected RedisSentinelConfiguration potentiallyGetSentinelConfig() {
|
||||
|
||||
if (this.sentinelConfiguration == null
|
||||
&& this.properties.getSentinel() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RedisSentinelConfiguration sentinelConfig = this.sentinelConfiguration;
|
||||
if (sentinelConfig == null && this.properties.getSentinel() != null) {
|
||||
sentinelConfig = new RedisSentinelConfiguration().master(this.properties
|
||||
.getSentinel().getMaster());
|
||||
sentinelConfig.setSentinels(createRedisNodesForSentinel(this.properties
|
||||
.getSentinel()));
|
||||
}
|
||||
return sentinelConfig;
|
||||
}
|
||||
|
||||
private List<RedisNode> createRedisNodesForSentinel(Sentinel sentinel) {
|
||||
|
||||
String[] nodeStrings = StringUtils.commaDelimitedListToStringArray(sentinel
|
||||
.getNodes());
|
||||
|
||||
List<RedisNode> nodes = new ArrayList<RedisNode>(nodeStrings.length);
|
||||
|
||||
for (String hostAndPort : nodeStrings) {
|
||||
String[] args = StringUtils.split(hostAndPort, ":");
|
||||
nodes.add(new RedisNode(args[0], Integer.valueOf(args[1])));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass(name = "org.apache.commons.pool2.impl.GenericObjectPool")
|
||||
protected static class RedisConnectionConfiguration extends
|
||||
RedisHAConnectionConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
JedisConnectionFactory factory = new JedisConnectionFactory(
|
||||
potentiallyGetSentinelConfig());
|
||||
applyConnectionFactoryProperties(factory, this.properties);
|
||||
return factory;
|
||||
}
|
||||
@ -74,10 +122,8 @@ public class RedisAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(GenericObjectPool.class)
|
||||
protected static class RedisPooledConnectionConfiguration {
|
||||
|
||||
@Autowired
|
||||
private RedisProperties properties;
|
||||
protected static class RedisPooledConnectionConfiguration extends
|
||||
RedisHAConnectionConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ -88,10 +134,14 @@ public class RedisAutoConfiguration {
|
||||
}
|
||||
|
||||
private JedisConnectionFactory createJedisConnectionFactory() {
|
||||
|
||||
if (this.properties.getPool() != null) {
|
||||
return new JedisConnectionFactory(jedisPoolConfig());
|
||||
return new JedisConnectionFactory(potentiallyGetSentinelConfig(),
|
||||
jedisPoolConfig());
|
||||
}
|
||||
else {
|
||||
return new JedisConnectionFactory(potentiallyGetSentinelConfig());
|
||||
}
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
|
||||
private JedisPoolConfig jedisPoolConfig() {
|
||||
|
@ -22,6 +22,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
* Configuration properties for Redis.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.redis")
|
||||
public class RedisProperties {
|
||||
@ -36,6 +37,8 @@ public class RedisProperties {
|
||||
|
||||
private RedisProperties.Pool pool;
|
||||
|
||||
private RedisProperties.Sentinel sentinel;
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
@ -76,7 +79,16 @@ public class RedisProperties {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
public void setSentinel(Sentinel sentinel) {
|
||||
this.sentinel = sentinel;
|
||||
}
|
||||
|
||||
public Sentinel getSentinel() {
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pool properties.
|
||||
*/
|
||||
public static class Pool {
|
||||
@ -122,4 +134,28 @@ public class RedisProperties {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for configuring redis sentinels.
|
||||
*/
|
||||
public static class Sentinel {
|
||||
|
||||
private String master;
|
||||
private String nodes;
|
||||
|
||||
public String getMaster() {
|
||||
return master;
|
||||
}
|
||||
|
||||
public void setMaster(String master) {
|
||||
this.master = master;
|
||||
}
|
||||
|
||||
public String getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setNodes(String nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.redis;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
@ -23,15 +26,20 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link RedisAutoConfiguration}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Christian Dupuis
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class RedisAutoConfigurationTests {
|
||||
|
||||
@ -74,4 +82,60 @@ public class RedisAutoConfigurationTests {
|
||||
.getPoolConfig().getMaxIdle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisConfigurationWithSentinel() throws Exception {
|
||||
List<String> sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380");
|
||||
|
||||
if (isAtLeastOneSentinelAvailable(sentinels)) {
|
||||
this.context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.redis.sentinel.master:mymaster");
|
||||
EnvironmentTestUtils.addEnvironment(
|
||||
this.context,
|
||||
"spring.redis.sentinel.nodes:"
|
||||
+ StringUtils.collectionToCommaDelimitedString(sentinels));
|
||||
this.context.register(RedisAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
assertTrue(this.context.getBean(JedisConnectionFactory.class)
|
||||
.isRedisSentinelAware());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAtLeastOneSentinelAvailable(List<String> sentinels) {
|
||||
for (String sentinel : sentinels) {
|
||||
if (isSentinelAvailable(sentinel)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSentinelAvailable(String node) {
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
String[] hostAndPort = node.split(":");
|
||||
jedis = new Jedis(hostAndPort[0], Integer.valueOf(hostAndPort[1]));
|
||||
jedis.connect();
|
||||
jedis.ping();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
if (jedis != null) {
|
||||
try {
|
||||
jedis.disconnect();
|
||||
jedis.close();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -261,6 +261,8 @@ content into your application; rather pick only the properties that you need.
|
||||
spring.redis.pool.min-idle=0
|
||||
spring.redis.pool.max-active=8
|
||||
spring.redis.pool.max-wait=-1
|
||||
spring.redis.sentinel.master= # name of Redis server
|
||||
spring.redis.sentinel.nodes= # comma-separated list of host:port pairs
|
||||
|
||||
# ACTIVEMQ ({sc-spring-boot-autoconfigure}/jms/activemq/ActiveMQProperties.{sc-ext}[ActiveMQProperties])
|
||||
spring.activemq.broker-url=tcp://localhost:61616 # connection URL
|
||||
|
@ -1 +1 @@
|
||||
provides: spring-data-redis,lettuce
|
||||
provides: spring-data-redis,jedis
|
Loading…
Reference in New Issue
Block a user