5. 传播

需要传播以确保源自同一根的活动一起收集在同一跟踪中。 最常见的传播方法是通过向接收跟踪上下文的服务器发送 RPC 请求,从客户端复制跟踪上下文。spring-doc.cn

例如,在进行下游 HTTP 调用时,其跟踪上下文将编码为请求标头并随其一起发送,如下图所示:spring-doc.cn

   Client Span                                                Server Span
┌──────────────────┐                                       ┌──────────────────┐
│                  │                                       │                  │
│   TraceContext   │           Http Request Headers        │   TraceContext   │
│ ┌──────────────┐ │          ┌───────────────────┐        │ ┌──────────────┐ │
│ │ TraceId      │ │          │ X─B3─TraceId      │        │ │ TraceId      │ │
│ │              │ │          │                   │        │ │              │ │
│ │ ParentSpanId │ │ Extract  │ X─B3─ParentSpanId │ Inject │ │ ParentSpanId │ │
│ │              ├─┼─────────>│                   ├────────┼>│              │ │
│ │ SpanId       │ │          │ X─B3─SpanId       │        │ │ SpanId       │ │
│ │              │ │          │                   │        │ │              │ │
│ │ Sampled      │ │          │ X─B3─Sampled      │        │ │ Sampled      │ │
│ └──────────────┘ │          └───────────────────┘        │ └──────────────┘ │
│                  │                                       │                  │
└──────────────────┘                                       └──────────────────┘

以上名称来自 B3 Propagation,它是 Brave 内置的,并在多种语言和框架中实现。spring-doc.cn

大多数用户使用框架拦截器来自动传播。 接下来的两个示例显示了它如何用于 Client 端和服务器。spring-doc.cn

以下示例显示了 Client 端传播的工作原理:spring-doc.cn

@Autowired Tracing tracing;

// configure a function that injects a trace context into a request
injector = tracing.propagation().injector(Request.Builder::addHeader);

// before a request is sent, add the current span's context to it
injector.inject(span.context(), request);

以下示例显示了服务器端传播的工作原理:spring-doc.cn

@Autowired Tracing tracing;
@Autowired Tracer tracer;

// configure a function that extracts the trace context from a request
extractor = tracing.propagation().extractor(Request::getHeader);

// when a server receives a request, it joins or starts a new trace
span = tracer.nextSpan(extractor.extract(request));

5.1. 传播额外的字段

有时,您需要传播额外的字段,例如请求 ID 或备用跟踪上下文。 例如,如果您在 Cloud Foundry 环境中,则可能需要传递请求 ID,如以下示例所示:spring-doc.cn

// when you initialize the builder, define the extra field you want to propagate
Tracing.newBuilder().propagationFactory(
  ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-vcap-request-id")
);

// later, you can tag that request ID or use it in log correlation
requestId = ExtraFieldPropagation.get("x-vcap-request-id");

您可能还需要传播您未使用的跟踪上下文。 例如,您可能位于 Amazon Web Services 环境中,但未向 X-Ray 报告数据。 为确保 X-Ray 能够正确共存,请传递其跟踪标头,如以下示例所示:spring-doc.cn

tracingBuilder.propagationFactory(
  ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-amzn-trace-id")
);
在 Spring Cloud Sleuth 中,跟踪构建器的所有元素都定义为 bean。所以如果你想传递一个自定义 ,就足够了 让您创建该类型的 bean,我们将在 bean 中设置它。Tracing.newBuilder()PropagationFactoryTracing

5.1.1. 带前缀的字段

如果它们遵循通用模式,您还可以为字段添加前缀。 以下示例显示了如何按原样传播字段,但将线上的 and 字段分别作为 和 发送:x-vcap-request-idcountry-codeuser-idx-baggage-country-codex-baggage-user-idspring-doc.cn

Tracing.newBuilder().propagationFactory(
  ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
                       .addField("x-vcap-request-id")
                       .addPrefixedFields("x-baggage-", Arrays.asList("country-code", "user-id"))
                       .build()
);

稍后,您可以调用以下代码来影响当前跟踪上下文的 country 代码:spring-doc.cn

ExtraFieldPropagation.set("x-country-code", "FO");
String countryCode = ExtraFieldPropagation.get("x-country-code");

或者,如果您有对跟踪上下文的引用,则可以显式使用它,如以下示例所示:spring-doc.cn

ExtraFieldPropagation.set(span.context(), "x-country-code", "FO");
String countryCode = ExtraFieldPropagation.get(span.context(), "x-country-code");
与以前版本的 Sleuth 不同的是,使用 Brave 时,您必须传递行李钥匙列表。 有以下属性可以实现此目的。 使用 ,您可以设置用于 HTTP 调用和消息收发的前缀键。 您还可以使用该属性传递传播到远程服务的带前缀键的列表,这些键不带任何前缀。 您还可以使用该属性传递将在本地传播但不会通过网络传播的列表键。 请注意,标题键前面没有 no。spring.sleuth.baggage-keysbaggage-baggage_spring.sleuth.propagation-keysspring.sleuth.local-keysx-

为了自动将行李值设置为 Slf4j 的 MDC,您必须设置 列表为 whitelisted 的属性 baggage 和 propagation keys 的 Carrier Keys 的 Carrier Keys 中。例如 将 luggage 的值设置为 MDC 中。spring.sleuth.log.slf4j.whitelisted-mdc-keysspring.sleuth.log.slf4j.whitelisted-mdc-keys=foofoospring-doc.cn

请注意,额外的字段将从下一个下游跟踪上下文开始传播并添加到 MDC 中。立即 将 extra 字段添加到 Current trace 上下文中的 MDC 中,将字段配置为 FLUSH on UPDATE:spring-doc.cn

@Bean
ScopeDecorator mdcScopeDecorator() {
    BaggageField countryCodeField = BaggageField.create("x-country-code");
    return MDCScopeDecorator.newBuilder()
            .clear()
            .add(SingleCorrelationField.newBuilder(countryCodeField)
                    .flushOnUpdate()
                    .build())
            .build();
}
请记住,向 MDC 添加条目会大大降低应用程序的性能!

如果您想将行李条目添加为标签,以便可以通过行李条目搜索 span,您可以使用列入白名单的行李键列表设置 的值。要禁用该功能,您必须传递 property 。spring.sleuth.propagation.tag.whitelisted-keysspring.sleuth.propagation.tag.enabled=falsespring-doc.cn

5.1.2. 提取传播的上下文

该 API 从传入请求或消息中读取跟踪标识符和采样状态。 运营商通常是请求对象或标头。TraceContext.Extractor<C>spring-doc.cn

此实用程序用于标准检测(例如 ),但也可用于自定义 RPC 或消息传递代码。HttpServerHandlerspring-doc.cn

TraceContextOrSamplingFlags通常只与 一起使用,除非您是 在客户端和服务器之间共享 SPAN ID。Tracer.nextSpan(extracted)spring-doc.cn

5.1.3. 在 Client 和 Server 之间共享 span ID

常规的插桩模式是创建一个表示 RPC 服务器端的 span。 在应用于传入客户端请求时,可能会返回完整的跟踪上下文。 尝试使用相同的 SPAN ID(如果支持)继续此跟踪,或创建子 SPAN 如果不是。共享 span ID 时,报告的数据将包含一个标志。Extractor.extractTracer.joinSpanspring-doc.cn

下图显示了 B3 传播的示例:spring-doc.cn

                              ┌───────────────────┐      ┌───────────────────┐
 Incoming Headers             │   TraceContext    │      │   TraceContext    │
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
│ X─B3-TraceId      │─────────┼─┼> TraceId      │ │──────┼─┼> TraceId      │ │
│                   │         │ │               │ │      │ │               │ │
│ X─B3-ParentSpanId │─────────┼─┼> ParentSpanId │ │──────┼─┼> ParentSpanId │ │
│                   │         │ │               │ │      │ │               │ │
│ X─B3-SpanId       │─────────┼─┼> SpanId       │ │──────┼─┼> SpanId       │ │
└───────────────────┘         │ │               │ │      │ │               │ │
                              │ │               │ │      │ │  Shared: true │ │
                              │ └───────────────┘ │      │ └───────────────┘ │
                              └───────────────────┘      └───────────────────┘

某些传播系统仅转发父 span ID,在 . 在这种情况下,始终会预置新的 span ID,并且传入上下文将确定父 ID。Propagation.Factory.supportsJoin() == falsespring-doc.cn

下图显示了 AWS 传播的示例:spring-doc.cn

                              ┌───────────────────┐      ┌───────────────────┐
 x-amzn-trace-id              │   TraceContext    │      │   TraceContext    │
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
│ Root              │─────────┼─┼> TraceId      │ │──────┼─┼> TraceId      │ │
│                   │         │ │               │ │      │ │               │ │
│ Parent            │─────────┼─┼> SpanId       │ │──────┼─┼> ParentSpanId │ │
└───────────────────┘         │ └───────────────┘ │      │ │               │ │
                              └───────────────────┘      │ │  SpanId: New  │ │
                                                         │ └───────────────┘ │
                                                         └───────────────────┘

注: 某些 span reporter 不支持共享 span ID。 例如,如果将 ,则应通过设置 来禁用联接。 这样做会强制在 上生成新的子 span。Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)Tracing.Builder.supportsJoin(false)Tracer.joinSpan()spring-doc.cn

5.1.4. 实现传播

TraceContext.Extractor<C>由插件实现。 在内部,此代码使用以下选项之一创建 union 类型 :Propagation.FactoryTraceContextOrSamplingFlagsspring-doc.cn

一些 implementations 从提取点(例如,读取传入 Headers)到注入(例如,写入传出 Headers)携带额外的数据。 例如,它可能带有请求 ID。 当 implementations 有额外的数据时,它们会按如下方式处理它:Propagationspring-doc.cn

  • 如果提取了 a,请将额外数据添加为 。TraceContextTraceContext.extra()spring-doc.cn

  • 否则,请将其添加为 ,这将处理。TraceContextOrSamplingFlags.extra()Tracer.nextSpanspring-doc.cn