【问题标题】:Sleuth does not include ParentSpan in b3 single header for message to RabbitMQSleuth 在 b3 单个标头中不包括 ParentSpan 以向 RabbitMQ 发送消息
【发布时间】:2020-12-16 15:48:46
【问题描述】:

我最近从 Spring Cloud Greenwich.SR2 迁移到了 Hoxton.SR9,并且正在使用 Spring Sleuth 来跟踪传入的 REST 调用到我的 Spring Boot 应用程序,这些调用会触发最终到达 RabbitMQ 的消息。

显然,Spring Sleuth 在这两个版本之间的主要区别之一是,现在只发送一个 b3 标头,而不是发送 3 个单独的消息标头用于跟踪、跨度和 parentSpan,格式为 {trace}-{span}-{sample}-{parentSpan}最后两个组件可选。

让我感到困惑的是,到达 RabbitMQ 的消息上的 b3 标头似乎从来没有 parentSpan 组件。例如顺序是这样的:

  1. 在应用 REST 控制器内:traceId=T、spanId=S1、parentSpanId=S0
  2. 从应用内发送事件之前:traceId=T、spanId=S2、parentSpanId=S1
  3. RabbitMQ 队列上的消息头:b3=T-S2-1

换句话说,消息头有正确的踪迹,但共享应用程序中发送事件上下文的跨度并且有no parentSpan。

这是预期的吗?我确信在以前版本的 Sleuth 中,我会在消息头中看到一个新的 spanId,并且 parentSpan 等于发送事件上下文中的 span。

我是否误解了正在发生的事情,或者(如果我的期望是明智的)有没有办法改变这种默认行为。

我有以下明确的侦探属性设置:

spring.sleuth.enabled=true
spring.messaging.enabled=true
spring.supportsJoin=false

(注意:我尝试使用默认的 supportJoin,但它似乎对缺少 parentSpanId 没有任何影响)。

调试信息

这是我通过调试器能够确定的:

  1. 我们将GenericMessage 传递给AbstractMessageChannel.send()
  2. 最终消息被传递到TracingChannelInterceptor.preSend()
  3. TracingChannelInterceptor 配置了一个DeferredInjector,其中setterMessageHeaderPropagation 的一个实例,injectorFactory 有一个producerInjectorFunction=SINGLE_NO_PARENT
  4. TracingChannelInterceptor.preSend() 方法调用 DeferredInjector.inject(span.context(), headers),这会导致 SINGLE_NO_PARENT 注入器将 b3 形式的 {traceId}-{spanId}-1 标头添加到传出消息中。

因此缺少 parentSpanId 似乎源于 TracingChannelInterceptor.injector 是一个 DeferredInjector,其生产者注入器是 SINGLE_NO_PARENT。

自定义配置

注册以下 bean 以覆盖默认传播设置会将行为更改为:

  1. 在应用 REST 控制器内:traceId=T、spanId=S1、parentSpanId=S0
  2. 从应用内发送事件之前:traceId=T、spanId=S2、parentSpanId=S1
  3. RabbitMQ 队列上的消息头:b3=T-S2-1-S1

但我想知道为什么有必要这样做?

@Configuration
public class B3Configuration {
  @Bean
  Propagation.Factory customPropagationFactory() {
    return B3Propagation.newFactoryBuilder()
        .injectFormat(B3Propagation.Format.SINGLE)
        .injectFormat(Span.Kind.CLIENT, B3Propagation.Format.SINGLE)
        .injectFormat(Span.Kind.CONSUMER, B3Propagation.Format.SINGLE)
        .injectFormat(Span.Kind.PRODUCER, B3Propagation.Format.SINGLE)
        .build();
  }
}

【问题讨论】:

  • 您是否碰巧有一个重现该问题的示例项目?
  • 嗨 Jonatan - 我现在没有任何东西,但如果有帮助的话,可能会整理一些简单的东西。这是一个非常普通的 Spring Boot 设置,带有 Spring Boot 2.3.7 和 Spring Cloud Hoxton.SR9。
  • 我现在远离我的源代码,但我确实尝试在我的 Spring Boot 应用程序中调试它,它达到了使用 Format.SINGLE_NO_PARENT 而不是 FORMAT.SINGLE 的地步传出 Spring 消息上的拦截器的上下文。抱歉,我现在不能更具体。
  • @JonatanIvanov 我在原始问题中添加了一些调试信息,希望对您有所帮助 - 如果您需要更多信息,请告诉我。
  • @JonatanIvanov 似乎可以通过注册问题文本中包含的 bean 来解决“问题”(如果是问题)。但我很惊讶我必须这样做才能让它以这种方式工作。

标签: spring-boot spring-cloud-sleuth


【解决方案1】:

默认的B3Propagation.Format好像改了,你可以在TraceBaggageConfiguration看到当前的:

// Note: Versions <2.2.3 use injectFormat(MULTI) for non-remote (ex spring-messaging)
// See #1643
static final Propagation.Factory B3_FACTORY = B3Propagation.newFactoryBuilder()
        .injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();

您可以关注详细信息,并在spring-cloud-sleuth#1643中进行了更改。

如果你想改变行为,我建议创建 BaggagePropagation.FactoryBuilder 而不是 Propagation.Factory,因为当 Sleuth 自动配置 Propagation.Factory 时,它会做更多事情,请参阅 TraceBaggageConfiguration

例如:

@Bean
BaggagePropagation.FactoryBuilder baggagePropagationFactoryBuilder() {
  return BaggagePropagation.newFactoryBuilder(
          B3Propagation.newFactoryBuilder()
                  .injectFormat(B3Propagation.Format.SINGLE)
                  .build()
  );
}

【讨论】:

  • 感谢您查看此@JonatanIvanov,以及有关使用BaggagePropagation.newFactoryBuilder 的有用建议。我查看了问题链接,但不确定结论 - 这种默认行为(使用 SINGLE_NO_PARENT)是否正确?结果对我来说似乎很奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 2016-07-13
相关资源
最近更新 更多