diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java index bb5e38439a1..f553e891994 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,10 +29,12 @@ 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.data.redis.RedisProperties.Cluster; import org.springframework.boot.autoconfigure.data.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.RedisClusterConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisSentinelConfiguration; @@ -76,6 +78,9 @@ public class RedisAutoConfiguration { @Autowired(required = false) private RedisSentinelConfiguration sentinelConfiguration; + @Autowired(required = false) + private RedisClusterConfiguration clusterConfiguration; + protected final JedisConnectionFactory applyProperties( JedisConnectionFactory factory) { factory.setHostName(this.properties.getHost()); @@ -104,21 +109,41 @@ public class RedisAutoConfiguration { return null; } + /** + * Create a {@link RedisClusterConfiguration} if necessary. + * @return {@literal null} if no cluster settings are set. + */ + protected final RedisClusterConfiguration getClusterConfiguration() { + if (this.clusterConfiguration != null) { + return this.clusterConfiguration; + } + if (this.properties.getCluster() == null) { + return null; + } + Cluster clusterProperties = this.properties.getCluster(); + RedisClusterConfiguration config = new RedisClusterConfiguration( + clusterProperties.getNodes()); + + if (clusterProperties.getMaxRedirects() != null) { + config.setMaxRedirects(config.getMaxRedirects()); + } + return config; + } + private List createSentinels(Sentinel sentinel) { - List sentinels = new ArrayList(); - String nodes = sentinel.getNodes(); - for (String node : StringUtils.commaDelimitedListToStringArray(nodes)) { + List nodes = new ArrayList(); + for (String node : StringUtils.commaDelimitedListToStringArray(sentinel.getNodes())) { try { String[] parts = StringUtils.split(node, ":"); Assert.state(parts.length == 2, "Must be defined as 'host:port'"); - sentinels.add(new RedisNode(parts[0], Integer.valueOf(parts[1]))); + nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1]))); } catch (RuntimeException ex) { throw new IllegalStateException( "Invalid redis sentinel " + "property '" + node + "'", ex); } } - return sentinels; + return nodes; } } @@ -135,9 +160,18 @@ public class RedisAutoConfiguration { @ConditionalOnMissingBean(RedisConnectionFactory.class) public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { - return applyProperties(new JedisConnectionFactory(getSentinelConfig())); + return applyProperties(createJedisConnectionFactory()); } + private JedisConnectionFactory createJedisConnectionFactory() { + if (getSentinelConfig() != null) { + return new JedisConnectionFactory(getSentinelConfig()); + } + if (getClusterConfiguration() != null) { + return new JedisConnectionFactory(getClusterConfiguration()); + } + return new JedisConnectionFactory(); + } } /** @@ -156,10 +190,16 @@ public class RedisAutoConfiguration { } private JedisConnectionFactory createJedisConnectionFactory() { - if (this.properties.getPool() != null) { - return new JedisConnectionFactory(getSentinelConfig(), jedisPoolConfig()); + JedisPoolConfig poolConfig = this.properties.getPool() != null ? jedisPoolConfig() + : new JedisPoolConfig(); + + if (getSentinelConfig() != null) { + return new JedisConnectionFactory(getSentinelConfig(), poolConfig); + } + if (getClusterConfiguration() != null) { + return new JedisConnectionFactory(getClusterConfiguration(), poolConfig); } - return new JedisConnectionFactory(getSentinelConfig()); + return new JedisConnectionFactory(poolConfig); } private JedisPoolConfig jedisPoolConfig() { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java index 08f778f48fe..162e2ebdf51 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.data.redis; +import java.util.List; + import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -57,6 +59,8 @@ public class RedisProperties { private Sentinel sentinel; + private Cluster cluster; + public int getDatabase() { return this.database; } @@ -113,6 +117,14 @@ public class RedisProperties { this.pool = pool; } + public Cluster getCluster() { + return this.cluster; + } + + public void setCluster(Cluster cluster) { + this.cluster = cluster; + } + /** * Pool properties. */ @@ -174,6 +186,42 @@ public class RedisProperties { public void setMaxWait(int maxWait) { this.maxWait = maxWait; } + + } + + /** + * Cluster properties. + */ + public static class Cluster { + + /** + * Comma-separated list of "host:port" pairs to bootstrap from. This represents + * an "initial" list of cluster nodes and is required to have at least one entry. + */ + private List nodes; + + /** + * Maximum number of redirects to follow when executing commands across the + * cluster. + */ + private Integer maxRedirects; + + public List getNodes() { + return this.nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public Integer getMaxRedirects() { + return this.maxRedirects; + } + + public void setMaxRedirects(Integer maxRedirects) { + this.maxRedirects = maxRedirects; + } + } /** @@ -206,5 +254,6 @@ public class RedisProperties { public void setNodes(String nodes) { this.nodes = nodes; } + } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java index 8249745256b..e91f1b057f0 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,7 +96,7 @@ public class RedisAutoConfigurationTests { @Test public void testRedisConfigurationWithSentinel() throws Exception { List sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380"); - if (isAtLeastOneSentinelAvailable(sentinels)) { + if (isAtLeastOneNodeAvailable(sentinels)) { load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:" + StringUtils.collectionToCommaDelimitedString(sentinels)); assertThat(this.context.getBean(JedisConnectionFactory.class) @@ -104,9 +104,21 @@ public class RedisAutoConfigurationTests { } } - private boolean isAtLeastOneSentinelAvailable(List sentinels) { - for (String sentinel : sentinels) { - if (isSentinelAvailable(sentinel)) { + @Test + public void testRedisConfigurationWithCluster() throws Exception { + List clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380"); + if (isAtLeastOneNodeAvailable(clusterNodes)) { + load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0), + "spring.redis.cluster.nodes[1]:" + clusterNodes.get(1)); + assertThat( + this.context.getBean(JedisConnectionFactory.class) + .getClusterConnection()).isNotNull(); + } + } + + private boolean isAtLeastOneNodeAvailable(List nodes) { + for (String node : nodes) { + if (isAvailable(node)) { return true; } } @@ -114,7 +126,7 @@ public class RedisAutoConfigurationTests { return false; } - private boolean isSentinelAvailable(String node) { + private boolean isAvailable(String node) { Jedis jedis = null; try { String[] hostAndPort = node.split(":"); diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index fdd2689a6bb..68dbb3d59d4 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -685,6 +685,8 @@ content into your application; rather pick only the properties that you need. spring.mongodb.embedded.version=2.6.10 # Version of Mongo to use. # REDIS ({sc-spring-boot-autoconfigure}/redis/RedisProperties.{sc-ext}[RedisProperties]) + spring.redis.cluster.max-redirects= # Maximum number of redirects to follow when executing commands across the cluster. + spring.redis.cluster.nodes= # Comma-separated list of "host:port" pairs to bootstrap from. spring.redis.database=0 # Database index used by the connection factory. spring.redis.host=localhost # Redis server host. spring.redis.password= # Login password of the redis server.