Add Lettuce Redis driver autoconfiguration

Introduce an alternative autoconfiguration if the lettuce Redis driver is
available. Add Lettuce-specific configuration property options
"spring.redis.lettuce.shutdown-timeout" to control the shutdown timeout
of the lettuce driver. Add documentation for the properties, the
supported drivers, and how to switch between drivers.

Split client-specific properties from spring.redis.pool to
spring.redis.jedis.pool and introduce spring.redis.lettuce namespace.
Deprecate spring.redis.pool property.

See gh-5311
pull/9066/head
Mark Paluch 9 years ago committed by Stephane Nicoll
parent 64dae5ec3a
commit 4563da9ac7

@ -244,6 +244,11 @@
<artifactId>jedis</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>

@ -481,6 +481,11 @@
<artifactId>jedis</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>

@ -22,7 +22,12 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
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 redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
@ -31,6 +36,7 @@ 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.Lettuce;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@ -41,6 +47,8 @@ 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.connection.lettuce.DefaultLettucePool;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
@ -58,31 +66,28 @@ 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)
public class RedisAutoConfiguration {
/**
* Redis connection configuration.
* Jedis Redis connection configuration.
*/
@Configuration
@ConditionalOnClass(GenericObjectPool.class)
protected static class RedisConnectionConfiguration {
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
protected static class JedisRedisConnectionConfiguration
extends RedisBaseConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;
public RedisConnectionConfiguration(RedisProperties properties,
public JedisRedisConnectionConfiguration(RedisProperties properties,
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
super(properties, sentinelConfiguration, clusterConfiguration);
this.properties = properties;
this.sentinelConfiguration = sentinelConfiguration.getIfAvailable();
this.clusterConfiguration = clusterConfiguration.getIfAvailable();
}
@Bean
@ -142,10 +147,239 @@ public class RedisAutoConfiguration {
}
}
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;
}
}
/**
* Lettuce Redis connection configuration.
*/
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, RedisClient.class,
RedisClusterClient.class })
protected static class LettuceRedisConnectionConfiguration
extends RedisBaseConfiguration {
private final RedisProperties properties;
public LettuceRedisConnectionConfiguration(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));
}
protected final LettuceConnectionFactory applyProperties(
LettuceConnectionFactory factory) {
configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}
if (this.properties.getLettuce() != null) {
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) {
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 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) {
String url = this.properties.getUrl();
try {
URI uri = new URI(url);
lettucePool.setHostName(uri.getHost());
lettucePool.setPort(uri.getPort());
if (uri.getUserInfo() != null) {
String password = uri.getUserInfo();
int index = password.lastIndexOf(":");
if (index >= 0) {
password = password.substring(index + 1);
}
lettucePool.setPassword(password);
}
}
catch (URISyntaxException ex) {
throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url,
ex);
}
}
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;
}
}
protected abstract static class RedisBaseConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;
protected RedisBaseConfiguration(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;
}
Sentinel sentinelProperties = this.properties.getSentinel();
if (sentinelProperties != null) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
@ -164,9 +398,11 @@ public class RedisAutoConfiguration {
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());
@ -194,29 +430,6 @@ public class RedisAutoConfiguration {
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;
}
}
/**
@ -229,7 +442,7 @@ public class RedisAutoConfiguration {
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
@ -239,7 +452,7 @@ public class RedisAutoConfiguration {
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;

@ -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.
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.data.redis;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
/**
* Configuration properties for Redis.
@ -27,6 +28,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
* @author Mark Paluch
*/
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
@ -72,6 +74,16 @@ public class RedisProperties {
private Cluster cluster;
/**
* Jedis client properties.
*/
private Jedis jedis;
/**
* Lettuce client properties.
*/
private Lettuce lettuce;
public int getDatabase() {
return this.database;
}
@ -136,6 +148,7 @@ public class RedisProperties {
this.sentinel = sentinel;
}
@DeprecatedConfigurationProperty(reason = "Moved to client-specific properties", replacement = "spring.redis.jedis.pool")
public Pool getPool() {
return this.pool;
}
@ -152,6 +165,22 @@ public class RedisProperties {
this.cluster = cluster;
}
public Jedis getJedis() {
return jedis;
}
public void setJedis(Jedis jedis) {
this.jedis = jedis;
}
public Lettuce getLettuce() {
return lettuce;
}
public void setLettuce(Lettuce lettuce) {
this.lettuce = lettuce;
}
/**
* Pool properties.
*/
@ -284,4 +313,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 for lettuce.
*/
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;
}
}
}

@ -0,0 +1,198 @@
/*
* 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 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 org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.connection.lettuce.DefaultLettucePool;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.*;
/**
* Tests for {@link RedisAutoConfiguration} using Lettuce as client.
*
* @author Mark Paluch
*/
public class LettuceRedisAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
}
@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");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getDatabase())
.isEqualTo(1);
}
@Test
public void testOverrideUrlRedisConfiguration() throws Exception {
load("spring.redis.host:foo", "spring.redis.password:xyz",
"spring.redis.port:1000", "spring.redis.ssl:true",
"spring.redis.url:redis://user:password@example:33");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName())
.isEqualTo("example");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getPort())
.isEqualTo(33);
assertThat(this.context.getBean(LettuceConnectionFactory.class).getPassword())
.isEqualTo("password");
assertThat(this.context.getBean(LettuceConnectionFactory.class).isUseSsl())
.isEqualTo(true);
}
@Test
public void testSslRedisConfiguration() throws Exception {
load("spring.redis.host:foo", "spring.redis.ssl:true");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(LettuceConnectionFactory.class).isUseSsl())
.isTrue();
}
@Test
public void testRedisConfigurationWithPool() throws Exception {
load("spring.redis.host:foo", "spring.redis.lettuce.pool.max-idle:1");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(getDefaultLettucePool(
this.context.getBean(LettuceConnectionFactory.class)).getHostName())
.isEqualTo("foo");
assertThat(getDefaultLettucePool(
this.context.getBean(LettuceConnectionFactory.class)).getPoolConfig()
.getMaxIdle()).isEqualTo(1);
}
@Test
public void testRedisConfigurationWithTimeout() throws Exception {
load("spring.redis.host:foo", "spring.redis.timeout:100");
assertThat(this.context.getBean(LettuceConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(LettuceConnectionFactory.class).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(LettuceConnectionFactory.class)
.isRedisSentinelAware()).isTrue();
}
}
@Test
public void testRedisConfigurationWithCluster() throws Exception {
List<String> clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380");
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) {
for (String node : nodes) {
if (isAvailable(node)) {
return true;
}
}
return false;
}
private boolean isAvailable(String node) {
RedisClient redisClient = null;
try {
String[] hostAndPort = node.split(":");
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 (redisClient != null) {
try {
redisClient.shutdown(0, 0, TimeUnit.SECONDS);
}
catch (Exception ex) {
// Continue
}
}
}
}
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.LettuceRedisConnectionConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
EnableRedisPropertiesConfiguration.class);
applicationContext.refresh();
return applicationContext;
}
@EnableConfigurationProperties(RedisProperties.class)
private static class EnableRedisPropertiesConfiguration {
}
}

@ -25,6 +25,7 @@ import org.junit.Test;
import redis.clients.jedis.Jedis;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
@ -42,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
* @author Mark Paluch
*/
public class RedisAutoConfigurationTests {
@ -93,7 +95,7 @@ public class RedisAutoConfigurationTests {
@Test
public void testRedisConfigurationWithPool() throws Exception {
load("spring.redis.host:foo", "spring.redis.pool.max-idle:1");
load("spring.redis.host:foo", "spring.redis.jedis.pool.max-idle:1");
assertThat(this.context.getBean(JedisConnectionFactory.class).getHostName())
.isEqualTo("foo");
assertThat(this.context.getBean(JedisConnectionFactory.class).getPoolConfig()
@ -173,10 +175,15 @@ public class RedisAutoConfigurationTests {
private AnnotationConfigApplicationContext doLoad(String... environment) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
applicationContext.register(RedisAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
applicationContext.register(RedisAutoConfiguration.JedisRedisConnectionConfiguration.class, RedisAutoConfiguration.RedisConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, EnableRedisPropertiesConfiguration.class);
applicationContext.refresh();
return applicationContext;
}
@EnableConfigurationProperties(RedisProperties.class)
private static class EnableRedisPropertiesConfiguration {
}
}

@ -125,6 +125,7 @@
<junit.version>4.12</junit.version>
<lettuce.version>5.0.0.BUILD-SNAPSHOT</lettuce.version>
<liquibase.version>3.5.3</liquibase.version>
<lettuce.version>5.0.0.BUILD-SNAPSHOT</lettuce.version>
<log4j2.version>2.8.2</log4j2.version>
<logback.version>1.2.3</logback.version>
<lombok.version>1.16.16</lombok.version>
@ -2337,6 +2338,11 @@
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>${lettuce.version}</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>

@ -748,6 +748,11 @@
<artifactId>jedis</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>

@ -852,21 +852,36 @@ content into your application; rather pick only the properties that you need.
spring.redis.host=localhost # Redis server host.
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.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.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.timeout=0 # Connection timeout in milliseconds.
spring.redis.ssl.enabled=false # Enable SSL support.
spring.redis.ssl.verify-peer=true # Enable SSL peer verification.
spring.redis.ssl.start-tls=false # Enable StartTLS support.
# REDIS JEDIS DRIVER
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.
# REDIS LETTUCE DRIVER
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.
# TRANSACTION ({sc-spring-boot-autoconfigure}/transaction/TransactionProperties.{sc-ext}[TransactionProperties])
spring.transaction.default-timeout= # Default transaction timeout in seconds.
spring.transaction.rollback-on-commit-failure= # Perform the rollback on commit failures.
# ----------------------------------------
# INTEGRATION PROPERTIES
# ----------------------------------------

@ -3074,3 +3074,44 @@ 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 Redis starter (`spring-boot-starter-data-redis` in particular) 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 provides a managed 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}")
// ...
}
----

@ -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