Java集成Brave/Zipkin初始化首条链路时currentSpan为空NPE问题求助
问题根因
tracer.currentSpan()的作用是获取当前线程上下文中已经激活的Span实例,你当前的应用作为链路首节点,程序启动后从未主动创建过任何Span,因此该方法返回null,调用span.context()自然触发空指针异常。
正确实现方案
作为链路首节点,你需要主动创建根Span,再基于该Span的上下文传递链路信息,完整的修正代码如下:
import io.netty.handler.logging.LogLevel; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; import reactor.netty.http.client.HttpClient; import reactor.netty.transport.logging.AdvancedByteBufFormat; import zipkin2.reporter.AsyncReporter; import zipkin2.reporter.brave.ZipkinSpanHandler; import zipkin2.reporter.urlconnection.URLConnectionSender; import brave.Tracing; import brave.Tracer; import brave.Span; import brave.propagation.B3Propagation; import brave.propagation.Propagation; import brave.propagation.TraceContext; import java.util.Map; public class QuestionApplication { private static final Propagation<String> B3_PROPAGATION = B3Propagation.newFactoryBuilder().build().get(); private static final TraceContext.Injector<WebClient.Builder> INJECTOR = B3_PROPAGATION.injector((builder, key, value) -> { builder.defaultHeader(key, value); }); public static void main(String[] args) { final var sender = URLConnectionSender.create("http://the-zipkin-instance.com:9411/api/v2/spans"); final var spanReporter = AsyncReporter.create(sender); final var tracing = Tracing.newBuilder() .localServiceName("thisIsTheClientApplicationWhoIsGeneratingTheFirstTraceId") .addSpanHandler(ZipkinSpanHandler.create(spanReporter)) .propagationFactory(B3Propagation.FACTORY) .build(); final Tracer tracer = tracing.tracer(); // 主动创建根Span(链路首节点) Span rootSpan = tracer.nextSpan().name("client-call-server").start(); try (Tracer.SpanInScope ignored = tracer.withSpanInScope(rootSpan)) { final var webClientBuilder = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(HttpClient.create() .wiretap("reactor.netty.http.client.HttpClient", LogLevel.INFO, AdvancedByteBufFormat.HEX_DUMP))); // 自动注入所有B3链路头(不需要手动拼X-B3-TraceId等字段) INJECTOR.inject(rootSpan.context(), webClientBuilder); WebClient webClient = webClientBuilder.build(); final var jsonPayload = Map.of("key", "someKey", "value", "someValue"); final var response = webClient.post() .uri("http://the-server.com/api/route") .body(BodyInserters.fromValue(jsonPayload)) .retrieve() .bodyToMono(String.class) .block(); System.out.println(response); } finally { // 必须结束Span,否则追踪数据不会上报 rootSpan.finish(); // 同步刷盘,避免JVM退出前异步Reporter还没发数据 spanReporter.flush(); tracing.close(); } } }
关键注意点
- 根Span必须通过
tracer.nextSpan()主动创建,无父节点的Span会自动生成全新的Trace ID - 不要手动拼接X-B3系列请求头,使用Brave提供的Propagation注入能力,会自动补全TraceId、SpanId、Sampled等所有标准链路头,避免传递漏项
- Span使用完成后必须调用
finish()方法,否则追踪数据不会被上报到Zipkin - 应用退出前需要手动调用AsyncReporter的
flush()方法,避免内存中缓存的未上报链路数据丢失
内容的提问来源于stack exchange,提问作者PatPanda
相关产品推荐
相关产品推荐

