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

Spring Cloud分布式追踪问题:Span无法正确关联(Spring Boot3)

问题描述

在Spring Boot 3集成Spring Cloud实现分布式追踪时,遇到跨线程请求的Span关联问题。由于Spring Cloud官网列出的Sleuth已不再维护,改用Micrometer Observation配合Zipkin实现追踪,但跨线程发起请求时,Zipkin里每个步骤都显示为独立Span,无法关联到同一个根Span。

尝试过以下代码处理,但没有效果:

Observation availabilityServiceObservation = Observation.createNotStarted("availabilityLookup",
                this.observationRegistry);
return availabilityServiceObservation.observe(() -> {
    AvailabilityResponse[] responses = clientBuilder.build().get().uri("http://availability-service/api/availability", i -> i.queryParam("names", items).build())
            .retrieve().bodyToMono(AvailabilityResponse[].class).block();

    assert responses != null;
    Boolean result = Arrays.stream(responses).allMatch(AvailabilityResponse::isAvailable);
    if (Boolean.TRUE.equals(result)) {
        bookingRepository.save(booking);
        return "Booking completed";
    } else {
        throw new IllegalArgumentException("Not available");
    }
});
解决方案

要解决跨线程的Span关联问题,核心是让包含追踪信息的上下文在线程间正确传递,以下是可行的处理方式:

  • 关联父Observation上下文创建子Span
    当前代码仅创建了新的Observation,但未关联父Span的上下文。需要先获取当前线程的父Observation上下文,再基于它创建子Observation:

    // 获取当前线程的父Observation上下文
    Observation parentObservation = Observation.getCurrentObservation();
    if (parentObservation != null) {
        // 基于父上下文创建关联的子Observation
        Observation availabilityServiceObservation = Observation.createNotStarted("availabilityLookup",
                parentObservation.getContext(), this.observationRegistry);
        return availabilityServiceObservation.observe(() -> {
            // 原有业务逻辑
            AvailabilityResponse[] responses = clientBuilder.build().get().uri("http://availability-service/api/availability", i -> i.queryParam("names", items).build())
                    .retrieve().bodyToMono(AvailabilityResponse[].class).block();
    
            assert responses != null;
            Boolean result = Arrays.stream(responses).allMatch(AvailabilityResponse::isAvailable);
            if (Boolean.TRUE.equals(result)) {
                bookingRepository.save(booking);
                return "Booking completed";
            } else {
                throw new IllegalArgumentException("Not available");
            }
        });
    }
    
  • 避免手动block(),利用Reactor上下文自动传递
    使用block()会破坏Reactor的异步上下文传递链,建议保持异步流程,让Micrometer自动处理Span关联:

    return Observation.createNotStarted("availabilityLookup", this.observationRegistry)
            .observe(() -> clientBuilder.build().get()
                    .uri("http://availability-service/api/availability", i -> i.queryParam("names", items).build())
                    .retrieve()
                    .bodyToMono(AvailabilityResponse[].class)
                    .map(responses -> {
                        Boolean result = Arrays.stream(responses).allMatch(AvailabilityResponse::isAvailable);
                        if (Boolean.TRUE.equals(result)) {
                            bookingRepository.save(booking);
                            return "Booking completed";
                        } else {
                            throw new IllegalArgumentException("Not available");
                        }
                    })
            );
    

    这种方式下Reactor会自动将父Span的上下文传递到异步线程,Zipkin就能正确将所有子Span关联到根Span下。

  • 确保ObservationRegistry为容器管理Bean
    检查ObservationRegistry是否是通过Spring容器注入的Bean,而非手动创建。只有容器管理的实例才能正确集成Zipkin的追踪配置,保证上下文传递的有效性。

内容的提问来源于stack exchange,提问作者M. Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:38:36