Skip to content

Commit a09992a

Browse files
committed
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.
1 parent b803384 commit a09992a

File tree

11 files changed

+578
-53
lines changed

11 files changed

+578
-53
lines changed

spring-boot-actuator/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@
253253
<artifactId>jedis</artifactId>
254254
<optional>true</optional>
255255
</dependency>
256+
<dependency>
257+
<groupId>biz.paluch.redis</groupId>
258+
<artifactId>lettuce</artifactId>
259+
<optional>true</optional>
260+
</dependency>
256261
<dependency>
257262
<groupId>org.springframework.amqp</groupId>
258263
<artifactId>spring-rabbit</artifactId>

spring-boot-autoconfigure/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,11 @@
437437
<artifactId>jedis</artifactId>
438438
<optional>true</optional>
439439
</dependency>
440+
<dependency>
441+
<groupId>biz.paluch.redis</groupId>
442+
<artifactId>lettuce</artifactId>
443+
<optional>true</optional>
444+
</dependency>
440445
<dependency>
441446
<groupId>org.liquibase</groupId>
442447
<artifactId>liquibase-core</artifactId>

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

Lines changed: 184 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020
import java.util.ArrayList;
2121
import java.util.List;
2222

23+
import com.lambdaworks.redis.RedisClient;
24+
import com.lambdaworks.redis.cluster.RedisClusterClient;
25+
import com.lambdaworks.redis.resource.ClientResources;
26+
import com.lambdaworks.redis.resource.DefaultClientResources;
27+
2328
import org.apache.commons.pool2.impl.GenericObjectPool;
29+
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
30+
2431
import redis.clients.jedis.Jedis;
2532
import redis.clients.jedis.JedisPoolConfig;
2633

@@ -29,6 +36,7 @@
2936
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3037
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3138
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Cluster;
39+
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce;
3240
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
3341
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3442
import org.springframework.context.annotation.Bean;
@@ -39,12 +47,15 @@
3947
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
4048
import org.springframework.data.redis.connection.jedis.JedisConnection;
4149
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
50+
import org.springframework.data.redis.connection.lettuce.DefaultLettucePool;
51+
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
4252
import org.springframework.data.redis.core.RedisOperations;
4353
import org.springframework.data.redis.core.RedisTemplate;
4454
import org.springframework.data.redis.core.StringRedisTemplate;
4555
import org.springframework.util.Assert;
4656
import org.springframework.util.StringUtils;
4757

58+
4859
/**
4960
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
5061
*
@@ -55,31 +66,27 @@
5566
* @author Phillip Webb
5667
* @author Eddú Meléndez
5768
* @author Stephane Nicoll
69+
* @author Mark Paluch
5870
*/
5971
@Configuration
60-
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
72+
@ConditionalOnClass({ RedisOperations.class })
6173
@EnableConfigurationProperties(RedisProperties.class)
6274
public class RedisAutoConfiguration {
6375

6476
/**
65-
* Redis connection configuration.
77+
* Jedis Redis connection configuration.
6678
*/
6779
@Configuration
68-
@ConditionalOnClass(GenericObjectPool.class)
69-
protected static class RedisConnectionConfiguration {
80+
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
81+
protected static class JedisRedisConnectionConfiguration extends RedisBaseConfiguration {
7082

7183
private final RedisProperties properties;
7284

73-
private final RedisSentinelConfiguration sentinelConfiguration;
74-
75-
private final RedisClusterConfiguration clusterConfiguration;
76-
77-
public RedisConnectionConfiguration(RedisProperties properties,
85+
public JedisRedisConnectionConfiguration(RedisProperties properties,
7886
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
7987
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
88+
super(properties, sentinelConfigurationProvider, clusterConfigurationProvider);
8089
this.properties = properties;
81-
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
82-
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
8390
}
8491

8592
@Bean
@@ -103,10 +110,174 @@ protected final JedisConnectionFactory applyProperties(
103110
return factory;
104111
}
105112

113+
private JedisConnectionFactory createJedisConnectionFactory() {
114+
115+
JedisPoolConfig poolConfig;
116+
if (this.properties.getJedis() != null && this.properties.getJedis().getPool() != null) {
117+
poolConfig = jedisPoolConfig(this.properties.getJedis().getPool());
118+
}
119+
else {
120+
poolConfig = new JedisPoolConfig();
121+
}
122+
123+
if (getSentinelConfig() != null) {
124+
return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
125+
}
126+
if (getClusterConfiguration() != null) {
127+
return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
128+
}
129+
return new JedisConnectionFactory(poolConfig);
130+
}
131+
132+
private JedisPoolConfig jedisPoolConfig(RedisProperties.Pool props) {
133+
JedisPoolConfig config = new JedisPoolConfig();
134+
config.setMaxTotal(props.getMaxActive());
135+
config.setMaxIdle(props.getMaxIdle());
136+
config.setMinIdle(props.getMinIdle());
137+
config.setMaxWaitMillis(props.getMaxWait());
138+
return config;
139+
}
140+
141+
}
142+
143+
/**
144+
* Lettuce Redis connection configuration.
145+
*/
146+
@Configuration
147+
@ConditionalOnClass({ GenericObjectPool.class, RedisClient.class, RedisClusterClient.class})
148+
protected static class LettuceRedisConnectionConfiguration extends RedisBaseConfiguration {
149+
150+
protected final RedisProperties properties;
151+
152+
public LettuceRedisConnectionConfiguration(RedisProperties properties,
153+
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
154+
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
155+
super(properties, sentinelConfigurationProvider, clusterConfigurationProvider);
156+
this.properties = properties;
157+
}
158+
159+
@Bean(destroyMethod = "shutdown")
160+
@ConditionalOnMissingBean(ClientResources.class)
161+
public DefaultClientResources lettuceClientResources() {
162+
return DefaultClientResources.create();
163+
}
164+
165+
@Bean
166+
@ConditionalOnMissingBean(RedisConnectionFactory.class)
167+
public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources)
168+
throws UnknownHostException {
169+
return applyProperties(createLettuceConnectionFactory(clientResources));
170+
}
171+
172+
protected final LettuceConnectionFactory applyProperties(
173+
LettuceConnectionFactory factory) {
174+
factory.setHostName(this.properties.getHost());
175+
factory.setPort(this.properties.getPort());
176+
if (this.properties.getPassword() != null) {
177+
factory.setPassword(this.properties.getPassword());
178+
}
179+
factory.setDatabase(this.properties.getDatabase());
180+
if (this.properties.getTimeout() > 0) {
181+
factory.setTimeout(this.properties.getTimeout());
182+
}
183+
if (this.properties.getSsl() != null) {
184+
factory.setUseSsl(this.properties.getSsl().isEnabled());
185+
factory.setStartTls(this.properties.getSsl().isStartTls());
186+
factory.setVerifyPeer(this.properties.getSsl().isVerifyPeer());
187+
}
188+
if (this.properties.getLettuce() != null) {
189+
190+
Lettuce lettuce = this.properties.getLettuce();
191+
if (lettuce.getShutdownTimeout() >= 0) {
192+
factory.setShutdownTimeout(this.properties.getLettuce().getShutdownTimeout());
193+
}
194+
}
195+
196+
return factory;
197+
}
198+
199+
protected final DefaultLettucePool applyProperties(
200+
DefaultLettucePool pool) {
201+
pool.setHostName(this.properties.getHost());
202+
pool.setPort(this.properties.getPort());
203+
if (this.properties.getPassword() != null) {
204+
pool.setPassword(this.properties.getPassword());
205+
}
206+
pool.setDatabase(this.properties.getDatabase());
207+
if (this.properties.getTimeout() > 0) {
208+
pool.setTimeout(this.properties.getTimeout());
209+
}
210+
211+
pool.afterPropertiesSet();
212+
213+
return pool;
214+
}
215+
216+
private LettuceConnectionFactory createLettuceConnectionFactory(ClientResources clientResources) {
217+
218+
if (getSentinelConfig() != null) {
219+
if (this.properties.getLettuce() != null && this.properties.getLettuce().getPool() != null) {
220+
DefaultLettucePool lettucePool = new DefaultLettucePool(getSentinelConfig());
221+
return new LettuceConnectionFactory(applyProperties(applyClientResources(lettucePool, clientResources)));
222+
}
223+
return applyClientResources(new LettuceConnectionFactory(getSentinelConfig()), clientResources);
224+
}
225+
226+
if (getClusterConfiguration() != null) {
227+
return applyClientResources(new LettuceConnectionFactory(getClusterConfiguration()), clientResources);
228+
}
229+
230+
if (this.properties.getLettuce() != null && this.properties.getLettuce().getPool() != null) {
231+
GenericObjectPoolConfig config = lettucePoolConfig(this.properties.getLettuce().getPool());
232+
DefaultLettucePool lettucePool = new DefaultLettucePool(this.properties.getHost(), this.properties.getPort(), config);
233+
return new LettuceConnectionFactory(applyProperties(applyClientResources(lettucePool, clientResources)));
234+
}
235+
236+
return applyClientResources(new LettuceConnectionFactory(), clientResources);
237+
}
238+
239+
private DefaultLettucePool applyClientResources(DefaultLettucePool lettucePool, ClientResources clientResources) {
240+
lettucePool.setClientResources(clientResources);
241+
return lettucePool;
242+
}
243+
244+
private LettuceConnectionFactory applyClientResources(LettuceConnectionFactory factory, ClientResources clientResources) {
245+
factory.setClientResources(clientResources);
246+
return factory;
247+
}
248+
249+
private GenericObjectPoolConfig lettucePoolConfig(RedisProperties.Pool props) {
250+
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
251+
config.setMaxTotal(props.getMaxActive());
252+
config.setMaxIdle(props.getMaxIdle());
253+
config.setMinIdle(props.getMinIdle());
254+
config.setMaxWaitMillis(props.getMaxWait());
255+
return config;
256+
}
257+
258+
}
259+
260+
protected abstract static class RedisBaseConfiguration {
261+
262+
protected final RedisProperties properties;
263+
264+
protected final RedisSentinelConfiguration sentinelConfiguration;
265+
266+
protected final RedisClusterConfiguration clusterConfiguration;
267+
268+
public RedisBaseConfiguration(RedisProperties properties,
269+
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
270+
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
271+
this.properties = properties;
272+
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
273+
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
274+
}
275+
106276
protected final RedisSentinelConfiguration getSentinelConfig() {
107277
if (this.sentinelConfiguration != null) {
108278
return this.sentinelConfiguration;
109279
}
280+
110281
Sentinel sentinelProperties = this.properties.getSentinel();
111282
if (sentinelProperties != null) {
112283
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
@@ -125,9 +296,11 @@ protected final RedisClusterConfiguration getClusterConfiguration() {
125296
if (this.clusterConfiguration != null) {
126297
return this.clusterConfiguration;
127298
}
299+
128300
if (this.properties.getCluster() == null) {
129301
return null;
130302
}
303+
131304
Cluster clusterProperties = this.properties.getCluster();
132305
RedisClusterConfiguration config = new RedisClusterConfiguration(
133306
clusterProperties.getNodes());
@@ -155,29 +328,6 @@ private List<RedisNode> createSentinels(Sentinel sentinel) {
155328
return nodes;
156329
}
157330

158-
private JedisConnectionFactory createJedisConnectionFactory() {
159-
JedisPoolConfig poolConfig = this.properties.getPool() != null
160-
? jedisPoolConfig() : new JedisPoolConfig();
161-
162-
if (getSentinelConfig() != null) {
163-
return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
164-
}
165-
if (getClusterConfiguration() != null) {
166-
return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
167-
}
168-
return new JedisConnectionFactory(poolConfig);
169-
}
170-
171-
private JedisPoolConfig jedisPoolConfig() {
172-
JedisPoolConfig config = new JedisPoolConfig();
173-
RedisProperties.Pool props = this.properties.getPool();
174-
config.setMaxTotal(props.getMaxActive());
175-
config.setMaxIdle(props.getMaxIdle());
176-
config.setMinIdle(props.getMinIdle());
177-
config.setMaxWaitMillis(props.getMaxWait());
178-
return config;
179-
}
180-
181331
}
182332

183333
/**

0 commit comments

Comments
 (0)