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
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
* @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 {
|
||||
*/
|
||||
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 {
|
||||
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 {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
public Jedis getJedis() {
|
||||
return this.jedis;
|
||||
}
|
||||
|
||||
public Lettuce getLettuce() {
|
||||
return this.lettuce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pool properties.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
* @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 {
|
||||
}
|
||||
|
||||
@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 {
|
||||
}
|
||||
|
||||
@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 {
|
||||
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 {
|
||||
@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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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}")
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user