Merge pull request #40484 from SOOHYUN-LIM

* gh-40484:
  Polish "Add a customizer for Lettuce's ClientOptions"
  Add a customizer for Lettuce's ClientOptions

Closes gh-40484
This commit is contained in:
Andy Wilkinson 2024-06-27 16:51:53 +01:00
commit 895fbd7057
4 changed files with 74 additions and 14 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@ -23,7 +23,9 @@ import org.springframework.data.redis.connection.lettuce.LettuceClientConfigurat
* Callback interface that can be implemented by beans wishing to customize the
* {@link LettuceClientConfiguration} through a {@link LettuceClientConfigurationBuilder
* LettuceClientConfiguration.LettuceClientConfigurationBuilder} whilst retaining default
* auto-configuration.
* auto-configuration. To customize only the
* {@link LettuceClientConfiguration#getClientOptions() client options} of the
* configuration, use {@link LettuceClientOptionsBuilderCustomizer} instead.
*
* @author Mark Paluch
* @since 2.0.0

View File

@ -0,0 +1,42 @@
/*
* Copyright 2012-2024 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 io.lettuce.core.ClientOptions;
import io.lettuce.core.ClientOptions.Builder;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link ClientOptions} of the {@link LettuceClientConfiguration} through a
* {@link Builder} whilst retaining default auto-configuration. To customize the entire
* configuration, use {@link LettuceClientConfigurationBuilderCustomizer} instead.
*
* @author Soohyun Lim
* @since 3.4.0
*/
@FunctionalInterface
public interface LettuceClientOptionsBuilderCustomizer {
/**
* Customize the {@link Builder}.
* @param clientOptionsBuilder the builder to customize
*/
void customize(Builder clientOptionsBuilder);
}

View File

@ -88,18 +88,22 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
@ConditionalOnMissingBean(RedisConnectionFactory.class)
@ConditionalOnThreading(Threading.PLATFORM)
LettuceConnectionFactory redisConnectionFactory(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> clientConfigurationBuilderCustomizers,
ObjectProvider<LettuceClientOptionsBuilderCustomizer> clientOptionsBuilderCustomizers,
ClientResources clientResources) {
return createConnectionFactory(builderCustomizers, clientResources);
return createConnectionFactory(clientConfigurationBuilderCustomizers, clientOptionsBuilderCustomizers,
clientResources);
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
@ConditionalOnThreading(Threading.VIRTUAL)
LettuceConnectionFactory redisConnectionFactoryVirtualThreads(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> clientConfigurationBuilderCustomizers,
ObjectProvider<LettuceClientOptionsBuilderCustomizer> clientOptionsBuilderCustomizers,
ClientResources clientResources) {
LettuceConnectionFactory factory = createConnectionFactory(builderCustomizers, clientResources);
LettuceConnectionFactory factory = createConnectionFactory(clientConfigurationBuilderCustomizers,
clientOptionsBuilderCustomizers, clientResources);
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("redis-");
executor.setVirtualThreads(true);
factory.setExecutor(executor);
@ -107,10 +111,11 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
}
private LettuceConnectionFactory createConnectionFactory(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> clientConfigurationBuilderCustomizers,
ObjectProvider<LettuceClientOptionsBuilderCustomizer> clientOptionsBuilderCustomizers,
ClientResources clientResources) {
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
getProperties().getLettuce().getPool());
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientConfigurationBuilderCustomizers,
clientOptionsBuilderCustomizers, clientResources, getProperties().getLettuce().getPool());
return createLettuceConnectionFactory(clientConfig);
}
@ -125,16 +130,17 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
}
private LettuceClientConfiguration getLettuceClientConfiguration(
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
ObjectProvider<LettuceClientConfigurationBuilderCustomizer> clientConfigurationBuilderCustomizers,
ObjectProvider<LettuceClientOptionsBuilderCustomizer> clientOptionsBuilderCustomizers,
ClientResources clientResources, Pool pool) {
LettuceClientConfigurationBuilder builder = createBuilder(pool);
applyProperties(builder);
if (StringUtils.hasText(getProperties().getUrl())) {
customizeConfigurationFromUrl(builder);
}
builder.clientOptions(createClientOptions());
builder.clientOptions(createClientOptions(clientOptionsBuilderCustomizers));
builder.clientResources(clientResources);
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
clientConfigurationBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}
@ -163,7 +169,8 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
}
}
private ClientOptions createClientOptions() {
private ClientOptions createClientOptions(
ObjectProvider<LettuceClientOptionsBuilderCustomizer> clientConfigurationBuilderCustomizers) {
ClientOptions.Builder builder = initializeClientOptionsBuilder();
Duration connectTimeout = getProperties().getConnectTimeout();
if (connectTimeout != null) {
@ -183,7 +190,9 @@ class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
}
builder.sslOptions(sslOptionsBuilder.build());
}
return builder.timeoutOptions(TimeoutOptions.enabled()).build();
builder.timeoutOptions(TimeoutOptions.enabled());
clientConfigurationBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}
private ClientOptions.Builder initializeClientOptionsBuilder() {

View File

@ -127,6 +127,8 @@ class RedisAutoConfigurationTests {
this.contextRunner.withUserConfiguration(CustomConfiguration.class).run((context) -> {
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
assertThat(cf.isUseSsl()).isTrue();
assertThat(cf.getClientConfiguration().getClientOptions())
.hasValueSatisfying((options) -> assertThat(options.isAutoReconnect()).isFalse());
});
}
@ -638,6 +640,11 @@ class RedisAutoConfigurationTests {
return LettuceClientConfigurationBuilder::useSsl;
}
@Bean
LettuceClientOptionsBuilderCustomizer clientOptionsBuilderCustomizer() {
return (builder) -> builder.autoReconnect(false);
}
}
@Configuration(proxyBeanMethods = false)