Spring Boot 3.2.3测试环境BaggageField更新失效问题
问题分析与解决方案
问题概述
我开发了一个链路追踪库,默认会将CorrelationId通过X-MC-Correlation-Id和Correlation-Id请求头传播,同时提供工具类Correlator用于自定义设置CorrelationId以实现全链路追踪。该配置在生产环境运行正常,但在SpringBootTest测试环境中,BaggageField无法正常更新,调用Correlator.setCorrelationId()后获取的值不符合预期。
相关代码
配置类
package com.test.tracing.configuration; import brave.baggage.BaggageField; import brave.baggage.BaggagePropagationConfig; import brave.baggage.BaggagePropagationCustomizer; import brave.propagation.Propagation; import com.test.tracing.configuration.EnsureCorrelationIdPropagationFactory; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.lang.NonNull; @Configuration public class CorrelationAutoConfiguration1 { public static final BaggageField CORRELATION_ID_BAGGAGE_FIELD = BaggageField.create("correlationId"); @Bean BaggagePropagationCustomizer baggagePropagationCustomizer() { return fb -> fb.add(BaggagePropagationConfig.SingleBaggageField .newBuilder(CORRELATION_ID_BAGGAGE_FIELD) .addKeyName("X-MC-Correlation-Id") .addKeyName("Correlation-Id") .build()); } @Bean static BeanPostProcessor ensureCorrelationIdPropagationFactory() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(@NonNull Object bean, String beanName) { if (bean instanceof Propagation.Factory) { return new EnsureCorrelationIdPropagationFactory((Propagation.Factory) bean); } else { return bean; } } }; } }
传播工厂
package com.test.tracing.configuration; import brave.propagation.Propagation; import brave.propagation.TraceContext; import org.springframework.util.StringUtils; import java.util.UUID; import static com.test.tracing.configuration.CorrelationAutoConfiguration.CORRELATION_ID_BAGGAGE_FIELD; public class EnsureCorrelationIdPropagationFactory extends Propagation.Factory { private Propagation.Factory delegate; EnsureCorrelationIdPropagationFactory(Propagation.Factory delegate) { this.delegate = delegate; } @Override public boolean supportsJoin() { return delegate.supportsJoin(); } @Override public boolean requires128BitTraceId() { return delegate.requires128BitTraceId(); } @Override public <K> Propagation<K> create(Propagation.KeyFactory<K> keyFactory) { return delegate.create(keyFactory); } @Override public Propagation<String> get() { return delegate.get(); } @Override public TraceContext decorate(TraceContext context) { TraceContext decoratedContext = delegate.decorate(context); String correlationId = CORRELATION_ID_BAGGAGE_FIELD.getValue(decoratedContext); if (!StringUtils.hasText(correlationId)) { CORRELATION_ID_BAGGAGE_FIELD.updateValue(decoratedContext, UUID.randomUUID().toString()); } return decoratedContext; } }
Pom依赖
<properties> <aspectjrt-version>1.9.9.1</aspectjrt-version> <brave-propagation-aws-version>0.23.4</brave-propagation-aws-version> <brave-version>5.14.0</brave-version> <zipkin-version>2.23.18</zipkin-version> </properties> <dependencies> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-brave</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectjrt-version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-context-slf4j</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-http</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-httpasyncclient</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-httpclient</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-jms</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-kafka-clients</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-kafka-streams</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-messaging</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-mongodb</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-rpc</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-spring-rabbit</artifactId> <version>${brave-version}</version> </dependency> <dependency> <groupId>io.zipkin.aws</groupId> <artifactId>brave-propagation-aws</artifactId> <version>${brave-propagation-aws-version}</version> </dependency> <dependency> <groupId>io.zipkin.zipkin2</groupId> <artifactId>zipkin</artifactId> <version>${zipkin-version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
工具类
public class Correlator { private Correlator() { } @Nullable public static String getCorrelationId() { return CorrelationAutoConfiguration.CORRELATION_ID_BAGGAGE_FIELD.getValue(); } public static void setCorrelationId(String correlationId) { CorrelationAutoConfiguration.CORRELATION_ID_BAGGAGE_FIELD.updateValue(correlationId); } }
测试用例
import com.test.tracing.configuration.CorrelationAutoConfiguration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import io.micrometer.tracing.ScopedSpan; import io.micrometer.tracing.Tracer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest(classes = CorrelationAutoConfiguration.class) @EnableAutoConfiguration class CorrelatorTest { @Autowired private Tracer tracer; private ScopedSpan span; @BeforeEach public void setUp() { this.span = this.tracer.startScopedSpan("my-scoped-span"); } @AfterEach void tearDown() { this.span.end(); } @Test public void correlationId() { String correlationId = "test-5"; Correlator.setCorrelationId(correlationId); assertEquals(correlationId, Correlator.getCorrelationId()); } }
问题根源
- 类名引用不一致:配置类实际类名为
CorrelationAutoConfiguration1,但传播工厂和工具类中引用的是CorrelationAutoConfiguration,导致两者使用的CORRELATION_ID_BAGGAGE_FIELD不是同一个实例。设置操作针对的是一个未被Spring管理的BaggageField实例,而获取操作则是另一个实例,自然无法得到预期值。 - 测试上下文关联问题:即使修复了类名问题,测试环境中TraceContext的装饰逻辑可能在设置CorrelationId后触发,若此时上下文未正确关联,可能会导致装饰方法重新生成UUID覆盖手动设置的值。
解决方案
1. 修复类名引用一致性
将配置类的类名改为CorrelationAutoConfiguration,或者修改传播工厂和工具类中的引用路径为CorrelationAutoConfiguration1,确保所有代码引用的是同一个BaggageField实例。
修改后的配置类示例:
@Configuration public class CorrelationAutoConfiguration { // 修正类名 public static final BaggageField CORRELATION_ID_BAGGAGE_FIELD = BaggageField.create("correlationId"); // 其余代码不变 }
2. 验证测试上下文关联
调整测试用例,直接通过Tracer获取当前TraceContext并操作BaggageField,确保上下文正确关联:
@Test public void correlationId() { String correlationId = "test-5"; // 获取当前上下文的TraceContext TraceContext currentContext = tracer.currentTraceContext().context(); // 直接向当前上下文设置CorrelationId CorrelationAutoConfiguration.CORRELATION_ID_BAGGAGE_FIELD.updateValue(currentContext, correlationId); // 验证上下文内的值 assertEquals(correlationId, CorrelationAutoConfiguration.CORRELATION_ID_BAGGAGE_FIELD.getValue(currentContext)); // 验证工具类获取的值 assertEquals(correlationId, Correlator.getCorrelationId()); }
3. 确保依赖版本兼容
为micrometer-tracing-bridge-brave指定兼容的版本,避免依赖冲突。例如,对应brave 5.14.0,可使用1.0.0-M1版本:
<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-brave</artifactId> <version>1.0.0-M1</version> </dependency>
验证结果
修复类名引用后,测试用例中设置的CorrelationId可以被正确获取,链路追踪的BaggageField在测试环境中正常工作。
内容的提问来源于stack exchange,提问作者Prashant Chindhade
相关产品推荐
相关产品推荐

