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;
|
package org.springframework.boot.autoconfigure.redis;
|
||||||
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
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.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
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.JedisConnection;
|
||||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||||
import org.springframework.data.redis.core.RedisOperations;
|
import org.springframework.data.redis.core.RedisOperations;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import redis.clients.jedis.Jedis;
|
import redis.clients.jedis.Jedis;
|
||||||
import redis.clients.jedis.JedisPoolConfig;
|
import redis.clients.jedis.JedisPoolConfig;
|
||||||
@ -43,6 +49,7 @@ import redis.clients.jedis.JedisPoolConfig;
|
|||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Christian Dupuis
|
* @author Christian Dupuis
|
||||||
|
* @author Christoph Strobl
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
|
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
|
||||||
@ -55,17 +62,58 @@ public class RedisAutoConfiguration {
|
|||||||
return new RedisProperties();
|
return new RedisProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
protected abstract static class RedisHAConnectionConfiguration {
|
||||||
@ConditionalOnMissingClass(name = "org.apache.commons.pool2.impl.GenericObjectPool")
|
|
||||||
protected static class RedisConnectionConfiguration {
|
|
||||||
|
|
||||||
@Autowired
|
@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
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
RedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
|
RedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
|
||||||
JedisConnectionFactory factory = new JedisConnectionFactory();
|
JedisConnectionFactory factory = new JedisConnectionFactory(
|
||||||
|
potentiallyGetSentinelConfig());
|
||||||
applyConnectionFactoryProperties(factory, this.properties);
|
applyConnectionFactoryProperties(factory, this.properties);
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
@ -74,10 +122,8 @@ public class RedisAutoConfiguration {
|
|||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(GenericObjectPool.class)
|
@ConditionalOnClass(GenericObjectPool.class)
|
||||||
protected static class RedisPooledConnectionConfiguration {
|
protected static class RedisPooledConnectionConfiguration extends
|
||||||
|
RedisHAConnectionConfiguration {
|
||||||
@Autowired
|
|
||||||
private RedisProperties properties;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ -88,10 +134,14 @@ public class RedisAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JedisConnectionFactory createJedisConnectionFactory() {
|
private JedisConnectionFactory createJedisConnectionFactory() {
|
||||||
|
|
||||||
if (this.properties.getPool() != null) {
|
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() {
|
private JedisPoolConfig jedisPoolConfig() {
|
||||||
|
@ -22,6 +22,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
* Configuration properties for Redis.
|
* Configuration properties for Redis.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Christoph Strobl
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = "spring.redis")
|
@ConfigurationProperties(prefix = "spring.redis")
|
||||||
public class RedisProperties {
|
public class RedisProperties {
|
||||||
@ -36,6 +37,8 @@ public class RedisProperties {
|
|||||||
|
|
||||||
private RedisProperties.Pool pool;
|
private RedisProperties.Pool pool;
|
||||||
|
|
||||||
|
private RedisProperties.Sentinel sentinel;
|
||||||
|
|
||||||
public String getHost() {
|
public String getHost() {
|
||||||
return this.host;
|
return this.host;
|
||||||
}
|
}
|
||||||
@ -76,7 +79,16 @@ public class RedisProperties {
|
|||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
|
public void setSentinel(Sentinel sentinel) {
|
||||||
|
this.sentinel = sentinel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sentinel getSentinel() {
|
||||||
|
return sentinel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Pool properties.
|
* Pool properties.
|
||||||
*/
|
*/
|
||||||
public static class Pool {
|
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;
|
package org.springframework.boot.autoconfigure.redis;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
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.connection.jedis.JedisConnectionFactory;
|
||||||
import org.springframework.data.redis.core.RedisOperations;
|
import org.springframework.data.redis.core.RedisOperations;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link RedisAutoConfiguration}.
|
* Tests for {@link RedisAutoConfiguration}.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Christian Dupuis
|
* @author Christian Dupuis
|
||||||
|
* @author Christoph Strobl
|
||||||
*/
|
*/
|
||||||
public class RedisAutoConfigurationTests {
|
public class RedisAutoConfigurationTests {
|
||||||
|
|
||||||
@ -74,4 +82,60 @@ public class RedisAutoConfigurationTests {
|
|||||||
.getPoolConfig().getMaxIdle());
|
.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.min-idle=0
|
||||||
spring.redis.pool.max-active=8
|
spring.redis.pool.max-active=8
|
||||||
spring.redis.pool.max-wait=-1
|
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])
|
# ACTIVEMQ ({sc-spring-boot-autoconfigure}/jms/activemq/ActiveMQProperties.{sc-ext}[ActiveMQProperties])
|
||||||
spring.activemq.broker-url=tcp://localhost:61616 # connection URL
|
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