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
相关产品推荐
相关产品推荐

