Configure automatic context propagation for Reactor

This commit adds a new configuration property,
`spring.reactor.context-propagation` that configures the context
propagation mode for Reactor operators. By default the value is set to
"AUTO" for reinstating automatically context values as ThreadLocals
within Reactor operators. The "LIMITED" mode restricts this feature ot
the "tap" and "handle" operators but has a slightly lower footprint.

Closes gh-34201
This commit is contained in:
Brian Clozel 2023-02-20 10:22:57 +01:00
parent bf15797014
commit 4da42c09a6
7 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012-2023 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.reactor;
import reactor.core.publisher.Hooks;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Reactor.
*
* @author Brian Clozel
* @since 3.0.2
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Hooks.class)
@EnableConfigurationProperties(ReactorProperties.class)
public class ReactorAutoConfiguration {
public ReactorAutoConfiguration(ReactorProperties properties) {
if (properties.getContextPropagation() == ReactorProperties.ContextPropagationMode.AUTO) {
Hooks.enableAutomaticContextPropagation();
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2012-2023 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.reactor;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for Reactor.
*
* @author Brian Clozel
* @since 3.0.3
*/
@ConfigurationProperties(prefix = "spring.reactor")
public class ReactorProperties {
/**
* Context Propagation support mode for Reactor operators.
*/
private ContextPropagationMode contextPropagation = ContextPropagationMode.AUTO;
public ContextPropagationMode getContextPropagation() {
return this.contextPropagation;
}
public void setContextPropagation(ContextPropagationMode contextPropagation) {
this.contextPropagation = contextPropagation;
}
public enum ContextPropagationMode {
/**
* Context Propagation is applied to all Reactor operators.
*/
AUTO,
/**
* Context Propagation is only applied to "tap" and "handle" Reactor operators.
*/
LIMITED
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2012-2023 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.
*/
/**
* Auto-configuration for Reactor.
*/
package org.springframework.boot.autoconfigure.reactor;

View File

@ -2176,6 +2176,10 @@
"level": "error"
}
},
{
"name": "spring.reactor.context-propagation",
"defaultValue": "auto"
},
{
"name": "spring.reactor.stacktrace-mode.enabled",
"description": "Whether Reactor should collect stacktrace information at runtime.",

View File

@ -93,6 +93,7 @@ org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration
org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration

View File

@ -0,0 +1,62 @@
/*
* Copyright 2012-2023 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.reactor;
import java.util.concurrent.atomic.AtomicReference;
import io.micrometer.context.ContextRegistry;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ReactorAutoConfiguration}.
*
* @author Brian Clozel
*/
class ReactorAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(ReactorAutoConfiguration.class));
private static final String THREADLOCAL_KEY = "ReactorAutoConfigurationTests";
private static final ThreadLocal<String> THREADLOCAL_VALUE = ThreadLocal.withInitial(() -> "failure");
@BeforeAll
static void initializeThreadLocalAccessors() {
ContextRegistry globalRegistry = ContextRegistry.getInstance();
globalRegistry.registerThreadLocalAccessor(THREADLOCAL_KEY, THREADLOCAL_VALUE);
}
@Test
void shouldConfigureAutomaticContextPropagation() {
AtomicReference<String> threadLocalValue = new AtomicReference<>();
this.contextRunner.run((applicationContext) -> {
Mono.just("test").doOnNext((element) -> threadLocalValue.set(THREADLOCAL_VALUE.get()))
.contextWrite(Context.of(THREADLOCAL_KEY, "success")).block();
assertThat(threadLocalValue.get()).isEqualTo("success");
});
}
}

View File

@ -21,4 +21,7 @@ For JDBC, the https://github.com/jdbc-observations/datasource-micrometer[Datasou
Read more about it https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/[in the reference documentation].
For R2DBC, the https://github.com/spring-projects-experimental/r2dbc-micrometer-spring-boot[Spring Boot Auto Configuration for R2DBC Observation] creates observations for R2DBC query invocations.
Observability support relies on the https://github.com/micrometer-metrics/context-propagation[Context Propagation library] for forwarding the current observation across threads and reactive pipelines.
`ThreadLocal` values are automatically reinstated in reactive operators, this behavior is controlled with the configprop:spring.reactor.context-propagation[] property.
The next sections will provide more details about logging, metrics and traces.