I have implemented a dynamic Redis data source, I find a interface named LettuceClientConfigurationBuilderCustomizer
that can help me build LettuceConnectionFactory
using custom configuration.
However, there is an obvious disadvantage of this interface.
When I try to use the method of setClientResource()
, I cannot get the context information of the LettuceConnectionFactory
.
This prevents me from creating Tracing(TracingContext
needs tags such as hostname
, database... ) in ClientResources
based on the current context (such as RedisProperties
).
I can submit a pull request that contains the builder interface that has been modified to add the redisProperties
parameter
Comment From: philwebb
Closing in favor of PR #21123
Comment From: philwebb
PR #21123 was closed because it breaks back compatibility. The suggested solution was:
@Bean
public JedisClientConfigurationBuilderCustomizer myCustomizer(RedisProperties redisProperties) {
return (builder) -> {
// use builder and redisProperties here
}
}
The response to that was:
When there is only one redis data source configuration in the system, there is no problem. If there are multiple configurations, it is not known which properties the current Builder is associated with.
@zmapleshine Can you please provide a small sample application that shows what you are trying to do? I especially interested in why you have multiple RedisProperties
beans.
Comment From: zmapleshine
@philwebb
This is a code segment in BeanDefinitionRegistryPostProcessor
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
DynamicRedisProperties dynamicRedisProperties = binder.bind("zmaple.redis", DynamicRedisProperties.class).get();
dynamicRedisProperties.getProperties().forEach((poolName, redisProperties) -> {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(DynamicConnectionFactoryBean.class)
.addConstructorArgValue(redisProperties)
.getRawBeanDefinition();
registry.registerBeanDefinition(PREFIX + "_" + poolName, beanDefinition);
});
}
The logic of initialization is basically the same as LettuceConnectionConfiguration
.
The following is a code example of custom configuration. Although it is not correct to inject LettuceConnectionFactory
, it is used to illustrate that I need context information to help me better set up ClientResource
,but the current interface cannot reach me Requirements.
i want set Tracing
in ClientResource ,in Tracing interface , I need to know the current redis configuration in order to get the hostname etc. as a trace tag。
see initialTraceContextProvider()
@Bean
public List<LettuceClientConfigurationBuilderCustomizer> lettuceClientConfigurationBuilderCustomizer(ObjectProvider<LettuceConnectionFactory> lettuceConnectionFactories) {
List<LettuceClientConfigurationBuilderCustomizer> lettuceClientConfigurationBuilderCustomizers = new ArrayList<>();
lettuceConnectionFactories.orderedStream().forEach(lettuceConnectionFactory -> {
lettuceClientConfigurationBuilderCustomizers.add(clientConfigurationBuilder -> {
clientConfigurationBuilder.clientResources(ClientResources.builder()
.tracing(new Tracing() {
@Override
public TracerProvider getTracerProvider() {
return () -> new Tracer() {
@Override
public Span nextSpan() {
return null;
}
@Override
public Span nextSpan(TraceContext traceContext) {
return null;
}
};
}
@Override
public TraceContextProvider initialTraceContextProvider() {
return () -> {
Span activeSpan = GlobalTracer.get().activeSpan();
io.opentracing.Tracer.SpanBuilder builder = GlobalTracer.get()
.buildSpan(lettuceConnectionFactory.getClientName())
.withTag("host",lettuceConnectionFactory.getHostName())
.withTag("database", lettuceConnectionFactory.getDatabase());
if (Objects.nonNull(activeSpan)) {
builder.asChildOf(activeSpan.context());
}
return new TraceContext() {
};
};
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean includeCommandArgsInSpanTags() {
return false;
}
@Override
public Endpoint createEndpoint(SocketAddress socketAddress) {
return new Endpoint() {
};
}
})
.build());
});
});
return lettuceClientConfigurationBuilderCustomizers;
}
Comment From: zmapleshine
Supplementary code snippets:
DynamicConnectionFactory
protected RedisConnectionFactory chooseDataSource() {
String poolName = DynamicRedisDatabaseHolder.peek();
if (StringUtils.isEmpty(poolName)) {
return primaryConnectionFactory;
}
return connectionFactoryMap.get(PREFIX + "_" + poolName);
}
Comment From: philwebb
I don't think we want to support such a complex setup directly in Spring Boot. I suspect that your lettuceClientConfigurationBuilderCustomizer
@Bean
method will also cause problems since it's returning a List
.
If you're wanting to dynamically configure multiple RedisConnectionFactory
beans, I think you're best approach would be to duplicate the code you need from LettuceConnectionConfiguration
and apply any customizations that you need directly at that point.
Comment From: zmapleshine
@philwebb
in LettuceConnectionConfiguration
,method getLettuceClientConfiguration
do a lot of configuration.
And I just want to pass the context information of the currently constructed lettuceConnectionFactory
when applying the custom configuration,now I cannot get the context when applying a custom configuration. If I want to achieve my goal, I have to copy the entire configuration. Is this a bit inappropriate? Is it possible to add context information to the custom interface?
Comment From: philwebb
I'm afraid not. We intentionally keep customizers as simple as possible. We're pretty consistent with this design and we don't think it's a good idea to tie a customizer to a properties object. Doing so would mean that a LettuceClientConfigurationBuilder
is (and always will be) tied to a RedisProperties
. That potentially paints us into a corner and makes it hard to break such a link in the future. We also prefer to keep *Properties
objects as a somewhat internal concept and we don't want them to be part of the more public *Customizer
APIs.