You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux中Micrometer与Zipkin2集成问题求助

Spring WebFlux 3.0.5 集成 Micrometer、Zipkin2 与 Brave 正确配置方案

一、修正 Gradle 依赖

确保依赖版本匹配 Spring WebFlux 3.0.5(对应 Spring Boot 3.0.x 系列),避免依赖冲突:

dependencies {
    // Spring WebFlux 核心
    implementation 'org.springframework.boot:spring-boot-starter-webflux:3.0.5'
    
    // Micrometer 与 Brave 桥接
    implementation 'io.micrometer:micrometer-tracing-bridge-brave:1.0.4'
    implementation 'io.micrometer:micrometer-registry-prometheus:1.10.5'
    
    // Zipkin 上报组件
    implementation 'io.zipkin.reporter2:zipkin-reporter-brave:2.16.3'
    
    // Netty 依赖(与 Spring Boot 内置版本对齐)
    implementation 'io.netty:netty-codec-http:4.1.94.Final'
}

二、解决 MeterRegistry 初始化 NPE 问题

之前的空指针是因为启动阶段 MDC 中无 TraceId,不能直接在初始化时绑定标签。WebFlux 是异步场景,需通过 Reactor Context 传递 TraceId,配合动态 MeterFilter 实现指标标签注入:

1. 核心追踪配置类

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.tracing.Tracer;
import io.zipkin.reporter2.AsyncZipkinSpanHandler;
import io.zipkin.reporter2.ZipkinReporter;
import io.zipkin.reporter2.okhttp3.OkHttpSender;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TracingConfig {

    // 绑定 Brave Tracer 到 Micrometer
    @Bean
    public Tracer braveTracer(com.github.kristofa.brave.Tracing braveTracing) {
        return new BraveTracer(braveTracing);
    }

    // 动态为指标添加 TraceId 标签(仅当存在活跃 Span 时生效)
    @Bean
    public MeterFilter traceIdMeterFilter(Tracer tracer) {
        return MeterFilter.commonTags(() -> {
            if (tracer.currentSpan() != null) {
                return java.util.Map.of("traceId", tracer.currentSpan().context().traceId());
            }
            return java.util.Map.of();
        });
    }

    // 配置 Zipkin 上报处理器
    @Bean
    public com.github.kristofa.brave.SpanHandler zipkinSpanHandler(ZipkinReporter<io.zipkin2.Span> reporter) {
        return AsyncZipkinSpanHandler.create(reporter);
    }

    // 配置 Zipkin 上报器
    @Bean
    public ZipkinReporter<io.zipkin2.Span> zipkinReporter() {
        return OkHttpSender.create("http://localhost:9411/api/v2/spans")
                .buildReporter();
    }

    // 初始化 Brave 核心追踪实例
    @Bean
    public com.github.kristofa.brave.Tracing braveTracing() {
        return com.github.kristofa.brave.Tracing.newBuilder()
                .localServiceName("your-service-name")
                .build();
    }
}

2. WebFlux 上下文传递 TraceId

在过滤器中将 TraceId 放入 Reactor Context,替代 ThreadLocal 版 MDC:

import io.micrometer.tracing.Tracer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Component
@Order(1)
public class TraceIdContextFilter implements WebFilter {

    private final Tracer tracer;

    public TraceIdContextFilter(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(exchange)
                .contextWrite(context -> {
                    if (tracer.currentSpan() != null) {
                        return context.put("traceId", tracer.currentSpan().context().traceId());
                    }
                    return context;
                });
    }
}

3. 日志中输出 TraceId

配置 Logback 读取 Reactor Context 中的 TraceId,修改 logback-spring.xml:

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%X{traceId}] [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

添加 Reactor 上下文到 MDC 的钩子:

import io.micrometer.tracing.Tracer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Hooks;

@Configuration
public class ReactorMdcConfig {

    @Bean
    public void reactorMdcHook(Tracer tracer) {
        Hooks.onEachOperator((scannable, coreSubscriber) -> {
            coreSubscriber.currentContext().getOrEmpty("traceId")
                    .ifPresent(traceId -> {
                        try (var scope = tracer.withSpan(tracer.currentSpan())) {
                            // 绑定到 MDC 供日志使用
                            org.slf4j.MDC.put("traceId", traceId);
                        }
                    });
            return coreSubscriber;
        });
    }
}

三、解决 localhost:9411 连接拒绝问题

该错误是因为 Zipkin 服务未启动或配置错误,两种解决方式:

1. 启动本地 Zipkin 服务

用 Docker 快速启动:

docker run -d -p 9411:9411 openzipkin/zipkin:2.24.3

或下载 Jar 包启动:

curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar

2. 临时禁用 Zipkin 上报(调试用)

在 application.yml 中设置采样率为 0:

management:
  tracing:
    sampling:
      probability: 0.0

四、CustomTraceBaggage 正确配置

如需自定义跨服务传递的元数据,通过 Brave 的 BaggagePropagation 配置:

import com.github.kristofa.brave.BaggagePropagation;
import com.github.kristofa.brave.Tracing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BaggageConfig {

    @Bean
    public Tracing braveTracing() {
        return Tracing.newBuilder()
                .localServiceName("your-service-name")
                .propagationFactory(BaggagePropagation.newFactoryBuilder()
                        // 添加自定义 Baggage 键
                        .add(BaggagePropagation.SingleBaggageField.create("custom-baggage-key"))
                        .build())
                .build();
    }
}

五、验证配置

  1. 启动应用后访问端点,查看日志是否输出 TraceId;
  2. 访问 Zipkin UI(http://localhost:9411/zipkin),搜索服务名查看 Trace 数据;
  3. 访问 /actuator/prometheus,检查指标是否包含 traceId 标签。

内容的提问来源于stack exchange,提问作者LowCool

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 06:00:04