Browse Source

Merge pull request #5311 from mp911de:issue/sd-redis-lettuce-driver-autoconfiguration

* pr/5311:
  Add missing tests
  Polish "Add Lettuce Redis driver autoconfiguration"
  Add Lettuce Redis driver autoconfiguration
pull/9066/head
Stephane Nicoll 9 years ago
parent
commit
291b0c5fa7
  1. 5
      spring-boot-autoconfigure/pom.xml
  2. 120
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java
  3. 197
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java
  4. 219
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java
  5. 153
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java
  6. 79
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java
  7. 176
      spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java
  8. 131
      spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java
  9. 10
      spring-boot-dependencies/pom.xml
  10. 5
      spring-boot-docs/pom.xml
  11. 15
      spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
  12. 43
      spring-boot-docs/src/main/asciidoc/howto.adoc
  13. 7
      spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

5
spring-boot-autoconfigure/pom.xml

@ -100,6 +100,11 @@ @@ -100,6 +100,11 @@
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.ipc</groupId>
<artifactId>reactor-netty</artifactId>

120
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/JedisConnectionConfiguration.java

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
/*
* Copyright 2012-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.net.UnknownHostException;
import org.apache.commons.pool2.impl.GenericObjectPool;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.util.StringUtils;
/**
* Redis connection configuration using Jedis.
*
* @author Mark Paluch
* @author Stephane Nicoll
*/
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private final RedisProperties properties;
JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
super(properties, sentinelConfiguration, clusterConfiguration);
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
private JedisConnectionFactory applyProperties(JedisConnectionFactory factory) {
configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}
factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) {
factory.setTimeout(this.properties.getTimeout());
}
return factory;
}
private void configureConnection(JedisConnectionFactory factory) {
if (StringUtils.hasText(this.properties.getUrl())) {
configureConnectionFromUrl(factory);
}
else {
factory.setHostName(this.properties.getHost());
factory.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
factory.setPassword(this.properties.getPassword());
}
}
}
private void configureConnectionFromUrl(JedisConnectionFactory factory) {
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
factory.setUseSsl(connectionInfo.isUseSsl());
factory.setHostName(connectionInfo.getHostName());
factory.setPort(connectionInfo.getPort());
if (connectionInfo.getPassword() != null) {
factory.setPassword(connectionInfo.getPassword());
}
}
private JedisConnectionFactory createJedisConnectionFactory() {
RedisProperties.Pool pool = this.properties.getJedis().getPool();
JedisPoolConfig poolConfig = pool != null
? jedisPoolConfig(pool) : new JedisPoolConfig();
if (getSentinelConfig() != null) {
return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
}
if (getClusterConfiguration() != null) {
return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
}
return new JedisConnectionFactory(poolConfig);
}
private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool pool) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(pool.getMaxActive());
config.setMaxIdle(pool.getMaxIdle());
config.setMinIdle(pool.getMinIdle());
config.setMaxWaitMillis(pool.getMaxWait());
return config;
}
}

197
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration.java

@ -0,0 +1,197 @@ @@ -0,0 +1,197 @@
/*
* Copyright 2012-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.net.UnknownHostException;
import io.lettuce.core.RedisClient;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.DefaultLettucePool;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.util.StringUtils;
/**
* Redis connection configuration using Lettuce.
*
* @author Mark Paluch
*/
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, RedisClient.class, RedisClusterClient.class })
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
private final RedisProperties properties;
LettuceConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
super(properties, sentinelConfigurationProvider, clusterConfigurationProvider);
this.properties = properties;
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(ClientResources.class)
public DefaultClientResources lettuceClientResources() {
return DefaultClientResources.create();
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public LettuceConnectionFactory redisConnectionFactory(
ClientResources clientResources) throws UnknownHostException {
return applyProperties(createLettuceConnectionFactory(clientResources));
}
private LettuceConnectionFactory applyProperties(LettuceConnectionFactory factory) {
configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}
if (this.properties.getLettuce() != null) {
RedisProperties.Lettuce lettuce = this.properties.getLettuce();
if (lettuce.getShutdownTimeout() >= 0) {
factory.setShutdownTimeout(
this.properties.getLettuce().getShutdownTimeout());
}
}
return factory;
}
private void configureConnection(LettuceConnectionFactory factory) {
if (StringUtils.hasText(this.properties.getUrl())) {
configureConnectionFromUrl(factory);
}
else {
factory.setHostName(this.properties.getHost());
factory.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
factory.setPassword(this.properties.getPassword());
}
factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) {
factory.setTimeout(this.properties.getTimeout());
}
}
}
private void configureConnectionFromUrl(LettuceConnectionFactory factory) {
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
factory.setUseSsl(connectionInfo.isUseSsl());
factory.setHostName(connectionInfo.getHostName());
factory.setPort(connectionInfo.getPort());
if (connectionInfo.getPassword() != null) {
factory.setPassword(connectionInfo.getPassword());
}
}
private DefaultLettucePool applyProperties(DefaultLettucePool pool) {
if (StringUtils.hasText(this.properties.getUrl())) {
configureConnectionFromUrl(pool);
}
else {
pool.setHostName(this.properties.getHost());
pool.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
pool.setPassword(this.properties.getPassword());
}
pool.setDatabase(this.properties.getDatabase());
}
if (this.properties.getTimeout() > 0) {
pool.setTimeout(this.properties.getTimeout());
}
pool.afterPropertiesSet();
return pool;
}
private void configureConnectionFromUrl(DefaultLettucePool lettucePool) {
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
lettucePool.setHostName(connectionInfo.getHostName());
lettucePool.setPort(connectionInfo.getPort());
if (connectionInfo.getPassword() != null) {
lettucePool.setPassword(connectionInfo.getPassword());
}
}
private LettuceConnectionFactory createLettuceConnectionFactory(
ClientResources clientResources) {
if (getSentinelConfig() != null) {
if (this.properties.getLettuce() != null
&& this.properties.getLettuce().getPool() != null) {
DefaultLettucePool lettucePool = new DefaultLettucePool(
getSentinelConfig());
return new LettuceConnectionFactory(applyProperties(
applyClientResources(lettucePool, clientResources)));
}
return applyClientResources(
new LettuceConnectionFactory(getSentinelConfig()),
clientResources);
}
if (getClusterConfiguration() != null) {
return applyClientResources(
new LettuceConnectionFactory(getClusterConfiguration()),
clientResources);
}
if (this.properties.getLettuce() != null
&& this.properties.getLettuce().getPool() != null) {
GenericObjectPoolConfig config = lettucePoolConfig(
this.properties.getLettuce().getPool());
DefaultLettucePool lettucePool = new DefaultLettucePool(
this.properties.getHost(), this.properties.getPort(), config);
return new LettuceConnectionFactory(applyProperties(
applyClientResources(lettucePool, clientResources)));
}
return applyClientResources(new LettuceConnectionFactory(), clientResources);
}
private DefaultLettucePool applyClientResources(DefaultLettucePool lettucePool,
ClientResources clientResources) {
lettucePool.setClientResources(clientResources);
return lettucePool;
}
private LettuceConnectionFactory applyClientResources(
LettuceConnectionFactory factory, ClientResources clientResources) {
factory.setClientResources(clientResources);
return factory;
}
private GenericObjectPoolConfig lettucePoolConfig(RedisProperties.Pool props) {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(props.getMaxActive());
config.setMaxIdle(props.getMaxIdle());
config.setMinIdle(props.getMinIdle());
config.setMaxWaitMillis(props.getMaxWait());
return config;
}
}

219
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java

@ -16,36 +16,19 @@ @@ -16,36 +16,19 @@
package org.springframework.boot.autoconfigure.data.redis;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.pool2.impl.GenericObjectPool;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import org.springframework.beans.factory.ObjectProvider;
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.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.context.annotation.Import;
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.Assert;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
@ -58,193 +41,33 @@ import org.springframework.util.StringUtils; @@ -58,193 +41,33 @@ import org.springframework.util.StringUtils;
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Marco Aust
* @author Mark Paluch
*/
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@ConditionalOnClass({ RedisOperations.class })
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class,
JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
/**
* Redis connection configuration.
*/
@Configuration
@ConditionalOnClass(GenericObjectPool.class)
protected static class RedisConnectionConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;
public RedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
this.properties = properties;
this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();
this.clusterConfiguration = clusterConfiguration.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
protected final JedisConnectionFactory applyProperties(
JedisConnectionFactory factory) {
configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}
factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) {
factory.setTimeout(this.properties.getTimeout());
}
return factory;
}
private void configureConnection(JedisConnectionFactory factory) {
if (StringUtils.hasText(this.properties.getUrl())) {
configureConnectionFromUrl(factory);
}
else {
factory.setHostName(this.properties.getHost());
factory.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
factory.setPassword(this.properties.getPassword());
}
}
}
private void configureConnectionFromUrl(JedisConnectionFactory factory) {
String url = this.properties.getUrl();
if (url.startsWith("rediss://")) {
factory.setUseSsl(true);
}
try {
URI uri = new URI(url);
factory.setHostName(uri.getHost());
factory.setPort(uri.getPort());
if (uri.getUserInfo() != null) {
String password = uri.getUserInfo();
int index = password.lastIndexOf(":");
if (index >= 0) {
password = password.substring(index + 1);
}
factory.setPassword(password);
}
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url,
ex);
}
}
protected final RedisSentinelConfiguration getSentinelConfig() {
if (this.sentinelConfiguration != null) {
return this.sentinelConfiguration;
}
Sentinel sentinelProperties = this.properties.getSentinel();
if (sentinelProperties != null) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
config.master(sentinelProperties.getMaster());
config.setSentinels(createSentinels(sentinelProperties));
return config;
}
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(clusterProperties.getMaxRedirects());
}
return config;
}
private List<RedisNode> createSentinels(Sentinel sentinel) {
List<RedisNode> 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'");
nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1])));
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Invalid redis sentinel " + "property '" + node + "'", ex);
}
}
return nodes;
}
private JedisConnectionFactory createJedisConnectionFactory() {
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(poolConfig);
}
private JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
RedisProperties.Pool props = this.properties.getPool();
config.setMaxTotal(props.getMaxActive());
config.setMaxIdle(props.getMaxIdle());
config.setMinIdle(props.getMinIdle());
config.setMaxWaitMillis(props.getMaxWait());
return config;
}
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
/**
* Standard Redis configuration.
*/
@Configuration
protected static class RedisConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}

153
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java

@ -0,0 +1,153 @@ @@ -0,0 +1,153 @@
/*
* Copyright 2012-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Base Redis connection configuration.
*
* @author Mark Paluch
* @author Stephane Nicoll
*/
abstract class RedisConnectionConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;
protected RedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
this.properties = properties;
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
}
protected final RedisSentinelConfiguration getSentinelConfig() {
if (this.sentinelConfiguration != null) {
return this.sentinelConfiguration;
}
RedisProperties.Sentinel sentinelProperties = this.properties.getSentinel();
if (sentinelProperties != null) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
config.master(sentinelProperties.getMaster());
config.setSentinels(createSentinels(sentinelProperties));
return config;
}
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;
}
RedisProperties.Cluster clusterProperties = this.properties.getCluster();
RedisClusterConfiguration config = new RedisClusterConfiguration(
clusterProperties.getNodes());
if (clusterProperties.getMaxRedirects() != null) {
config.setMaxRedirects(clusterProperties.getMaxRedirects());
}
return config;
}
private List<RedisNode> createSentinels(RedisProperties.Sentinel sentinel) {
List<RedisNode> 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'");
nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1])));
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Invalid redis sentinel " + "property '" + node + "'", ex);
}
}
return nodes;
}
protected ConnectionInfo parseUrl(String url) {
try {
URI uri = new URI(url);
boolean useSsl = (url.startsWith("rediss://"));
String password = null;
if (uri.getUserInfo() != null) {
password = uri.getUserInfo();
int index = password.lastIndexOf(":");
if (index >= 0) {
password = password.substring(index + 1);
}
}
return new ConnectionInfo(uri, useSsl, password);
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException("Malformed url '" + url + "'", ex);
}
}
protected static class ConnectionInfo {
private final URI uri;
private final boolean useSsl;
private final String password;
public ConnectionInfo(URI uri, boolean useSsl, String password) {
this.uri = uri;
this.useSsl = useSsl;
this.password = password;
}
public boolean isUseSsl() {
return this.useSsl;
}
public String getHostName() {
return this.uri.getHost();
}
public int getPort() {
return this.uri.getPort();
}
public String getPassword() {
return this.password;
}
}
}

79
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 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.
@ -27,6 +27,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @@ -27,6 +27,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
* @author Mark Paluch
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
@ -66,12 +68,14 @@ public class RedisProperties { @@ -66,12 +68,14 @@ public class RedisProperties {
*/
private int timeout;
private Pool pool;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
public int getDatabase() {
return this.database;
}
@ -136,14 +140,6 @@ public class RedisProperties { @@ -136,14 +140,6 @@ public class RedisProperties {
this.sentinel = sentinel;
}
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
public Cluster getCluster() {
return this.cluster;
}
@ -152,6 +148,14 @@ public class RedisProperties { @@ -152,6 +148,14 @@ public class RedisProperties {
this.cluster = cluster;
}
public Jedis getJedis() {
return this.jedis;
}
public Lettuce getLettuce() {
return this.lettuce;
}
/**
* Pool properties.
*/
@ -284,4 +288,57 @@ public class RedisProperties { @@ -284,4 +288,57 @@ public class RedisProperties {
}
/**
* Jedis client properties.
*/
public static class Jedis {
/**
* Jedis pool configuration.
*/
private Pool pool;
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
}
/**
* Lettuce client properties.
*/
public static class Lettuce {
/**
* Shutdown timeout in milliseconds.
*/
private int shutdownTimeout = 2000;
/**
* Lettuce pool configuration.
*/
private Pool pool;
public int getShutdownTimeout() {
return this.shutdownTimeout;
}
public void setShutdownTimeout(int shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
public Pool getPool() {
return this.pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
}
}

176
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java

@ -0,0 +1,176 @@ @@ -0,0 +1,176 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.data.redis;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import redis.clients.jedis.Jedis;
import org.springframework.boot.junit.runner.classpath.ClassPathExclusions;
import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link RedisAutoConfiguration} when Lettuce is not on the classpath.
*
* @author Mark Paluch
* @author Stephane Nicoll
*/
@RunWith(ModifiedClassPathRunner.class)
@ClassPathExclusions("lettuce-core-*.jar")
public class RedisAutoConfigurationJedisTests {
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testOverrideRedisConfiguration() throws Exception {
load("spring.redis.host:foo", "spring.redis.database:1");
JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("foo");
assertThat(cf.getDatabase()).isEqualTo(1);
assertThat(cf.getPassword()).isNull();
assertThat(cf.isUseSsl()).isFalse();
}
@Test
public void testRedisUrlConfiguration() throws Exception {
load("spring.redis.host:foo",
"spring.redis.url:redis://user:password@example:33");
JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("example");
assertThat(cf.getPort()).isEqualTo(33);
assertThat(cf.getPassword()).isEqualTo("password");
assertThat(cf.isUseSsl()).isFalse();
}
@Test
public void testOverrideUrlRedisConfiguration() throws Exception {
load("spring.redis.host:foo", "spring.redis.password:xyz",
"spring.redis.port:1000", "spring.redis.ssl:false",
"spring.redis.url:rediss://user:password@example:33");
JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("example");
assertThat(cf.getPort()).isEqualTo(33);
assertThat(cf.getPassword()).isEqualTo("password");
assertThat(cf.isUseSsl()).isTrue();
}
@Test
public void testRedisConfigurationWithPool() throws Exception {
load("spring.redis.host:foo", "spring.redis.jedis.pool.min-idle:1",
"spring.redis.jedis.pool.max-idle:4",
"spring.redis.jedis.pool.max-active:16",
"spring.redis.jedis.pool.max-wait:2000");
JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("foo");
assertThat(cf.getPoolConfig().getMinIdle()).isEqualTo(1);
assertThat(cf.getPoolConfig().getMaxIdle()).isEqualTo(4);
assertThat(cf.getPoolConfig().getMaxTotal()).isEqualTo(16);
assertThat(cf.getPoolConfig().getMaxWaitMillis())
.isEqualTo(2000);
}
@Test
public void testRedisConfigurationWithTimeout() throws Exception {
load("spring.redis.host:foo", "spring.redis.timeout:100");
JedisConnectionFactory cf = this.context.getBean(JedisConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("foo");
assertThat(cf.getTimeout()).isEqualTo(100);
}
@Test
public void testRedisConfigurationWithSentinel() throws Exception {
List<String> sentinels = Arrays.asList("127.0.0.1:26379", "127.0.0.1:26380");
if (isAtLeastOneNodeAvailable(sentinels)) {
load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:"
+ StringUtils.collectionToCommaDelimitedString(sentinels));
assertThat(this.context.getBean(JedisConnectionFactory.class)
.isRedisSentinelAware()).isTrue();
}
}
@Test
public void testRedisConfigurationWithCluster() throws Exception {
List<String> 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 void load(String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
ctx.register(RedisAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
}
private boolean isAtLeastOneNodeAvailable(List<String> nodes) {
for (String node : nodes) {
if (isAvailable(node)) {
return true;
}
}
return false;
}
private boolean isAvailable(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
}
}
}
}
}

131
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java

@ -18,18 +18,21 @@ package org.springframework.boot.autoconfigure.data.redis; @@ -18,18 +18,21 @@ package org.springframework.boot.autoconfigure.data.redis;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.DefaultLettucePool;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -42,16 +45,13 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -42,16 +45,13 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
* @author Mark Paluch
* @author Stephane Nicoll
*/
public class RedisAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
}
@After
public void close() {
if (this.context != null) {
@ -60,7 +60,7 @@ public class RedisAutoConfigurationTests { @@ -60,7 +60,7 @@ public class RedisAutoConfigurationTests {
}
@Test
public void testDefaultRedisConfiguration() throws Exception {
public void testDefaultRedisConfiguration() {
load();
assertThat(this.context.getBean("redisTemplate", RedisOperations.class))
.isNotNull();
@ -68,45 +68,60 @@ public class RedisAutoConfigurationTests { @@ -68,45 +68,60 @@ public class RedisAutoConfigurationTests {
}
@Test
public void testOverrideRedisConfiguration() throws Exception {
public void testOverrideRedisConfiguration() {
load("spring.redis.host:foo", "spring.redis.database:1");
assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(JedisConnectionFactory.class).getDatabase())
.isEqualTo(1);
LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("foo");
assertThat(cf.getDatabase()).isEqualTo(1);
assertThat(cf.getPassword()).isNull();
assertThat(cf.isUseSsl()).isFalse();
}
@Test
public void testOverrideUrlRedisConfiguration() throws Exception {
load("spring.redis.host:foo", "spring.redis.password:xyz",
"spring.redis.port:1000", "spring.redis.ssl:true",
public void testRedisUrlConfiguration() throws Exception {
load("spring.redis.host:foo",
"spring.redis.url:redis://user:password@example:33");
assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName())
.isEqualTo("example");
assertThat(this.context.getBean(JedisConnectionFactory.class).getPort())
.isEqualTo(33);
assertThat(this.context.getBean(JedisConnectionFactory.class).getPassword())
.isEqualTo("password");
assertThat(this.context.getBean(JedisConnectionFactory.class).isUseSsl())
.isEqualTo(true);
LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("example");
assertThat(cf.getPort()).isEqualTo(33);
assertThat(cf.getPassword()).isEqualTo("password");
assertThat(cf.isUseSsl()).isFalse();
}
@Test
public void testOverrideUrlRedisConfiguration() {
load("spring.redis.host:foo", "spring.redis.password:xyz",
"spring.redis.port:1000", "spring.redis.ssl:false",
"spring.redis.url:rediss://user:password@example:33");
LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("example");
assertThat(cf.getPort()).isEqualTo(33);
assertThat(cf.getPassword()).isEqualTo("password");
assertThat(cf.isUseSsl()).isTrue();
}
@Test
public void testRedisConfigurationWithPool() throws Exception {
load("spring.redis.host:foo", "spring.redis.pool.max-idle:1");
assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(JedisConnectionFactory.class).getPoolConfig()
.getMaxIdle()).isEqualTo(1);
load("spring.redis.host:foo", "spring.redis.lettuce.pool.min-idle:1",
"spring.redis.lettuce.pool.max-idle:4",
"spring.redis.lettuce.pool.max-active:16",
"spring.redis.lettuce.pool.max-wait:2000");
LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("foo");
assertThat(getDefaultLettucePool(cf).getHostName()).isEqualTo("foo");
assertThat(getDefaultLettucePool(cf).getPoolConfig().getMinIdle()).isEqualTo(1);
assertThat(getDefaultLettucePool(cf).getPoolConfig().getMaxIdle()).isEqualTo(4);
assertThat(getDefaultLettucePool(cf).getPoolConfig().getMaxTotal()).isEqualTo(16);
assertThat(getDefaultLettucePool(cf).getPoolConfig().getMaxWaitMillis())
.isEqualTo(2000);
}
@Test
public void testRedisConfigurationWithTimeout() throws Exception {
load("spring.redis.host:foo", "spring.redis.timeout:100");
assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(JedisConnectionFactory.class).getTimeout())
.isEqualTo(100);
LettuceConnectionFactory cf = this.context.getBean(LettuceConnectionFactory.class);
assertThat(cf.getHostName()).isEqualTo("foo");
assertThat(cf.getTimeout()).isEqualTo(100);
}
@Test
@ -115,7 +130,7 @@ public class RedisAutoConfigurationTests { @@ -115,7 +130,7 @@ public class RedisAutoConfigurationTests {
if (isAtLeastOneNodeAvailable(sentinels)) {
load("spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:"
+ StringUtils.collectionToCommaDelimitedString(sentinels));
assertThat(this.context.getBean(JedisConnectionFactory.class)
assertThat(this.context.getBean(LettuceConnectionFactory.class)
.isRedisSentinelAware()).isTrue();
}
}
@ -123,12 +138,14 @@ public class RedisAutoConfigurationTests { @@ -123,12 +138,14 @@ public class RedisAutoConfigurationTests {
@Test
public void testRedisConfigurationWithCluster() throws Exception {
List<String> 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();
}
load("spring.redis.cluster.nodes[0]:" + clusterNodes.get(0),
"spring.redis.cluster.nodes[1]:" + clusterNodes.get(1));
assertThat(this.context.getBean(LettuceConnectionFactory.class)
.getClusterConnection()).isNotNull();
}
private DefaultLettucePool getDefaultLettucePool(LettuceConnectionFactory factory) {
return (DefaultLettucePool) ReflectionTestUtils.getField(factory, "pool");
}
private boolean isAtLeastOneNodeAvailable(List<String> nodes) {
@ -142,22 +159,23 @@ public class RedisAutoConfigurationTests { @@ -142,22 +159,23 @@ public class RedisAutoConfigurationTests {
}
private boolean isAvailable(String node) {
Jedis jedis = null;
RedisClient redisClient = null;
try {
String[] hostAndPort = node.split(":");
jedis = new Jedis(hostAndPort[0], Integer.valueOf(hostAndPort[1]));
jedis.connect();
jedis.ping();
redisClient = RedisClient.create(new RedisURI(hostAndPort[0],
Integer.valueOf(hostAndPort[1]), 10, TimeUnit.SECONDS));
StatefulRedisConnection<String, String> connection = redisClient.connect();
connection.sync().ping();
connection.close();
return true;
}
catch (Exception ex) {
return false;
}
finally {
if (jedis != null) {
if (redisClient != null) {
try {
jedis.disconnect();
jedis.close();
redisClient.shutdown(0, 0, TimeUnit.SECONDS);
}
catch (Exception ex) {
// Continue
@ -167,16 +185,11 @@ public class RedisAutoConfigurationTests { @@ -167,16 +185,11 @@ public class RedisAutoConfigurationTests {
}
private void load(String... environment) {
this.context = doLoad(environment);
}
private AnnotationConfigApplicationContext doLoad(String... environment) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.register(RedisAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
applicationContext.refresh();
return applicationContext;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
ctx.register(RedisAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
}
}

10
spring-boot-dependencies/pom.xml

@ -570,11 +570,6 @@ @@ -570,11 +570,6 @@
<artifactId>antlr</artifactId>
<version>${antlr2.version}</version>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
@ -830,6 +825,11 @@ @@ -830,6 +825,11 @@
<artifactId>metrics-servlets</artifactId>
<version>${dropwizard-metrics.version}</version>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>

5
spring-boot-docs/pom.xml

@ -172,6 +172,11 @@ @@ -172,6 +172,11 @@
<artifactId>metrics-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.projectreactor.ipc</groupId>
<artifactId>reactor-netty</artifactId>

15
spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc

@ -850,15 +850,20 @@ content into your application; rather pick only the properties that you need. @@ -850,15 +850,20 @@ content into your application; rather pick only the properties that you need.
spring.redis.database=0 # Database index used by the connection factory.
spring.redis.url= # Connection URL, will override host, port and password (user will be ignored), e.g. redis://user:password@example.com:6379
spring.redis.host=localhost # Redis server host.
spring.redis.jedis.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.jedis.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.jedis.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.jedis.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
spring.redis.lettuce.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.lettuce.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.lettuce.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.lettuce.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
spring.redis.lettuce.shutdown-timeout=2000 # Shutdown timeout in milliseconds.
spring.redis.password= # Login password of the redis server.
spring.redis.ssl=false # Enable SSL support.
spring.redis.pool.max-active=8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
spring.redis.pool.max-idle=8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
spring.redis.pool.max-wait=-1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
spring.redis.pool.min-idle=0 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
spring.redis.port=6379 # Redis server port.
spring.redis.sentinel.master= # Name of Redis server.
spring.redis.sentinel.nodes= # Comma-separated list of host:port pairs.
spring.redis.ssl=false # Enable SSL support.
spring.redis.timeout=0 # Connection timeout in milliseconds.
# TRANSACTION ({sc-spring-boot-autoconfigure}/transaction/TransactionProperties.{sc-ext}[TransactionProperties])

43
spring-boot-docs/src/main/asciidoc/howto.adoc

@ -3074,3 +3074,46 @@ but the rest of it is normal for a Spring application in Servlet 2.5. Example: @@ -3074,3 +3074,46 @@ but the rest of it is normal for a Spring application in Servlet 2.5. Example:
In this example we are using a single application context (the one created by the context
listener) and attaching it to the `DispatcherServlet` using an init parameter. This is
normal in a Spring Boot application (you normally only have one application context).
[[howto-use-lettuce-instead-of-jedis]]
=== Use Lettuce instead of Jedis
The Spring Boot starter (`spring-boot-starter-data-redis`) uses
https://github.com/xetorthio/jedis/[Jedis] by default. You need to exclude that dependency
and include the https://github.com/lettuce-io/lettuce-core/[Lettuce] one instead. Spring
Boot manages that dependency to help make this process as easy as possible.
Example in Maven:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
----
Example in Gradle:
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
----
configurations {
compile.exclude module: "jedis"
}
dependencies {
compile("io.lettuce:lettuce-core:{lettuce.version}")
// ...
}
----

7
spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

@ -3262,10 +3262,11 @@ http://projects.spring.io/spring-data[projects.spring.io/spring-data]. @@ -3262,10 +3262,11 @@ http://projects.spring.io/spring-data[projects.spring.io/spring-data].
=== Redis
http://redis.io/[Redis] is a cache, message broker and richly-featured key-value store.
Spring Boot offers basic auto-configuration for the
https://github.com/xetorthio/jedis/[Jedis] client library and abstractions on top of it
provided by https://github.com/spring-projects/spring-data-redis[Spring Data Redis]. There
https://github.com/xetorthio/jedis/[Jedis] and and https://github.com/mp911de/lettuce/[Lettuce]
client library and abstractions on top of it provided by
https://github.com/spring-projects/spring-data-redis[Spring Data Redis]. There
is a `spring-boot-starter-data-redis` '`Starter`' for collecting the dependencies in a
convenient way.
convenient way that uses https://github.com/xetorthio/jedis/[Jedis] by default.

Loading…
Cancel
Save