Skip to content

Add lettuce Redis driver autoconfiguration. #5311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions spring-boot-actuator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
5 changes: 5 additions & 0 deletions spring-boot-autoconfigure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
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;

Expand All @@ -31,6 +36,7 @@
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;
Expand All @@ -41,6 +47,8 @@
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;
Expand All @@ -58,31 +66,28 @@
* @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
Expand Down Expand Up @@ -142,10 +147,239 @@ private void configureConnectionFromUrl(JedisConnectionFactory factory) {
}
}

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();
Expand All @@ -164,9 +398,11 @@ 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());
Expand Down Expand Up @@ -194,29 +430,6 @@ private List<RedisNode> createSentinels(Sentinel sentinel) {
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;
}

}

/**
Expand All @@ -229,7 +442,7 @@ protected static class RedisConfiguration {
@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;
Expand All @@ -239,7 +452,7 @@ public RedisTemplate<Object, Object> redisTemplate(
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
Expand Down
Loading