Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand All @@ -24,9 +24,12 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.ClientType;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
Expand All @@ -41,6 +44,7 @@
*
* @author Mark Paluch
* @author Stephane Nicoll
* @author Weix Sun
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
Expand All @@ -56,13 +60,13 @@ class JedisConnectionConfiguration extends RedisConnectionConfiguration {

@Bean
JedisConnectionFactory redisConnectionFactory(
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
return createJedisConnectionFactory(builderCustomizers);
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers, Environment environment) {
return createJedisConnectionFactory(builderCustomizers, environment);
}

private JedisConnectionFactory createJedisConnectionFactory(
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers);
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers, Environment environment) {
JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(builderCustomizers, environment);
if (getSentinelConfig() != null) {
return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration);
}
Expand All @@ -73,12 +77,9 @@ private JedisConnectionFactory createJedisConnectionFactory(
}

private JedisClientConfiguration getJedisClientConfiguration(
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers) {
ObjectProvider<JedisClientConfigurationBuilderCustomizer> builderCustomizers, Environment environment) {
JedisClientConfigurationBuilder builder = applyProperties(JedisClientConfiguration.builder());
RedisProperties.Pool pool = getProperties().getJedis().getPool();
if (pool != null) {
applyPooling(pool, builder);
}
createPoolingBuilder(builder, environment, getProperties().getJedis().getPool());
if (StringUtils.hasText(getProperties().getUrl())) {
customizeConfigurationFromUrl(builder);
}
Expand All @@ -95,6 +96,19 @@ private JedisClientConfigurationBuilder applyProperties(JedisClientConfiguration
return builder;
}

private void createPoolingBuilder(JedisClientConfigurationBuilder builder, Environment environment, Pool pool) {
final boolean poolEnabled = environment.getProperty("spring.redis.jedis.pool.enabled", Boolean.class, true);
if (poolEnabled) {
applyPooling(pool, builder);
return;
}
final boolean isSentinelConfig = (getSentinelConfig() != null);
// Jedis Sentinel cannot operate without a pool.
if (isSentinelConfig) {
throw new RedisClientPoolingException(ClientType.JEDIS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like something new to me and unrelated to configure the pool automatically. I am happy to consider this case at a later stage but for now please revert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it. Regarding this, I will report it in another issue or pr.

}
}

private void applyPooling(RedisProperties.Pool pool,
JedisClientConfiguration.JedisClientConfigurationBuilder builder) {
builder.usePooling().poolConfig(jedisPoolConfig(pool));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -33,24 +33,28 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.ClientType;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce.Cluster.Refresh;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
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.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
* Redis connection configuration using Lettuce.
*
* @author Mark Paluch
* @author Andy Wilkinson
* @author Weix Sun
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisClient.class)
Expand All @@ -73,9 +77,9 @@ DefaultClientResources lettuceClientResources() {
@ConditionalOnMissingBean(RedisConnectionFactory.class)
LettuceConnectionFactory redisConnectionFactory(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ClientResources clientResources) {
ClientResources clientResources, Environment environment) {
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
getProperties().getLettuce().getPool());
getProperties().getLettuce().getPool(), environment);
return createLettuceConnectionFactory(clientConfig);
}

Expand All @@ -91,27 +95,38 @@ private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientCon

private LettuceClientConfiguration getLettuceClientConfiguration(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ClientResources clientResources, Pool pool) {
LettuceClientConfigurationBuilder builder = createBuilder(pool);
ClientResources clientResources, Pool pool, Environment environment) {
LettuceClientConfigurationBuilder builder = createBuilder(environment, pool);

applyProperties(builder);
if (StringUtils.hasText(getProperties().getUrl())) {
customizeConfigurationFromUrl(builder);
}
builder.clientOptions(createClientOptions());
builder.clientResources(clientResources);

builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}

private LettuceClientConfigurationBuilder createBuilder(Pool pool) {
if (pool == null) {
return LettuceClientConfiguration.builder();
private LettuceClientConfigurationBuilder createBuilder(Environment environment, Pool pool) {
final boolean poolEnabled = environment.getProperty("spring.redis.lettuce.pool.enabled", Boolean.class, true);
if (poolEnabled) {
poolingFailureAnalyzerEvaluation();
return new PoolBuilderFactory().createBuilder(pool);
}
return LettuceClientConfiguration.builder();
}

private void poolingFailureAnalyzerEvaluation() {
final boolean pool2Present = ClassUtils.isPresent(PoolBuilderFactory.GENERIC_OBJECT_POOL_CONFIG_CLASS_NAME,
null);
if (!pool2Present) {
throw new RedisClientPoolingException(ClientType.LETTUCE);
}
return new PoolBuilderFactory().createBuilder(pool);
}

private LettuceClientConfigurationBuilder applyProperties(
LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
private void applyProperties(LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
if (getProperties().isSsl()) {
builder.useSsl();
}
Expand All @@ -127,7 +142,6 @@ private LettuceClientConfigurationBuilder applyProperties(
if (StringUtils.hasText(getProperties().getClientName())) {
builder.clientName(getProperties().getClientName());
}
return builder;
}

private ClientOptions createClientOptions() {
Expand Down Expand Up @@ -168,6 +182,8 @@ private void customizeConfigurationFromUrl(LettuceClientConfiguration.LettuceCli
*/
private static class PoolBuilderFactory {

private static final String GENERIC_OBJECT_POOL_CONFIG_CLASS_NAME = "org.apache.commons.pool2.impl.GenericObjectPoolConfig";

LettuceClientConfigurationBuilder createBuilder(Pool properties) {
return LettucePoolingClientConfiguration.builder().poolConfig(getPoolConfig(properties));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2012-2021 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
*
* https://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 org.springframework.boot.autoconfigure.data.redis.RedisProperties.ClientType;
import org.springframework.util.StringUtils;

/**
* Exception thrown when a Redis client pooling failure.
*
* @author Weix Sun
*/
class RedisClientPoolingException extends RuntimeException {

static final String JEDIS_SENTINEL_POOLING_EXPECTED_MESSAGE = "Jedis Sentinel cannot operate without a pool";

static final String LETTUCE_LACK_COMMONSPOOL2_EXPECTED_MESSAGE = "Lettuce pool cannot enable if \"commons-pool2\" don't exists on the classpath";

private final ClientType clientType;

RedisClientPoolingException(ClientType clientType) {
super(buildMessage(clientType, null));
this.clientType = clientType;
}

RedisClientPoolingException(ClientType clientType, String message) {
super(buildMessage(clientType, message));
this.clientType = clientType;
}

ClientType getClientType() {
return this.clientType;
}

private static String buildMessage(ClientType clientType, String message) {
if (StringUtils.hasText(message)) {
return message;
}
return getDefaultPoolingExceptionMessage(clientType);
}

private static String getDefaultPoolingExceptionMessage(ClientType clientType) {
if (ClientType.JEDIS.equals(clientType)) {
return JEDIS_SENTINEL_POOLING_EXPECTED_MESSAGE;
}
if (ClientType.LETTUCE.equals(clientType)) {
return LETTUCE_LACK_COMMONSPOOL2_EXPECTED_MESSAGE;
}
return "";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2012-2021 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
*
* https://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 org.springframework.boot.autoconfigure.data.redis.RedisProperties.ClientType;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.util.StringUtils;

/**
* A {@code FailureAnalyzer} that performs analysis of failures caused by a
* {@link RedisClientPoolingException}.
*
* @author Weix Sun
*/
class RedisClientPoolingFailureAnalyzer extends AbstractFailureAnalyzer<RedisClientPoolingException> {

@Override
protected FailureAnalysis analyze(Throwable rootFailure, RedisClientPoolingException cause) {
return new FailureAnalysis(getDescription(cause.getClientType(), cause.getMessage()),
getAction(cause.getClientType(), cause.getMessage()), cause);
}

private String getDescription(ClientType clientType, String message) {
if (StringUtils.hasText(message)) {
StringBuilder detailsMessage = new StringBuilder();
detailsMessage.append(String.format("%s failed to pooling. Details are %s.", clientType.name(), message));
return detailsMessage.toString();
}
return "";
}

private String getAction(ClientType clientType, String message) {
if (StringUtils.hasText(message)) {
if (isGetDefaultPoolingFailureAction(message)) {
return getDefaultPoolingFailureAction(clientType);
}
}
return "";
}

private boolean isGetDefaultPoolingFailureAction(String message) {
return RedisClientPoolingException.JEDIS_SENTINEL_POOLING_EXPECTED_MESSAGE.equals(message)
|| RedisClientPoolingException.LETTUCE_LACK_COMMONSPOOL2_EXPECTED_MESSAGE.equals(message);
}

private String getDefaultPoolingFailureAction(ClientType clientType) {
if (ClientType.JEDIS.equals(clientType)) {
return "Set spring.redis.jedis.pool.enabled=true instead of spring.redis.jedis.pool.enabled=false or delete this configuration item.(Default: true)";
}
if (ClientType.LETTUCE.equals(clientType)) {
return "Add \"commons-pool2\" dependency to the classpath.";
}
return "";
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -396,16 +396,12 @@ public static class Jedis {
/**
* Jedis pool configuration.
*/
private Pool pool;
private final Pool pool = new Pool();

public Pool getPool() {
return this.pool;
}

public void setPool(Pool pool) {
this.pool = pool;
}

}

/**
Expand All @@ -421,7 +417,7 @@ public static class Lettuce {
/**
* Lettuce pool configuration.
*/
private Pool pool;
private final Pool pool = new Pool();

private final Cluster cluster = new Cluster();

Expand All @@ -437,10 +433,6 @@ public Pool getPool() {
return this.pool;
}

public void setPool(Pool pool) {
this.pool = pool;
}

public Cluster getCluster() {
return this.cluster;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,18 @@
"replacement": "spring.reactor.debug-agent.enabled"
}
},
{
"name": "spring.redis.lettuce.pool.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable lettuce pool. If true, make sure \"commons-pool2\" exists on the classpath.",
"defaultValue": true
},
{
"name": "spring.redis.jedis.pool.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable jedis pool.",
"defaultValue": true
},
{
"name": "spring.resources.chain.gzipped",
"type": "java.lang.Boolean",
Expand Down
Loading