5. 传播

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

例如,当进行下游 HTTP 调用时,其跟踪上下文将编码为请求标头并随之发送,如下图所示:spring-doc.cadn.net.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.cadn.net.cn

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

以下示例显示了客户端传播的工作原理:spring-doc.cadn.net.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.cadn.net.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 环境中,则可能需要传递请求标识,如以下示例所示:spring-doc.cadn.net.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.cadn.net.cn

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

5.1.1. 前缀字段

如果它们遵循通用模式,您还可以为字段添加前缀。 以下示例演示如何传播x-vcap-request-id字段按原样发送country-codeuser-id线上的字段作为x-baggage-country-codex-baggage-user-id分别:spring-doc.cadn.net.cn

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

稍后,您可以调用以下代码来影响当前跟踪上下文的国家/地区代码:spring-doc.cadn.net.cn

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

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

ExtraFieldPropagation.set(span.context(), "x-country-code", "FO");
String countryCode = ExtraFieldPropagation.get(span.context(), "x-country-code");
与之前版本的 Sleuth 的不同之处在于,在 Brave 中,您必须通过行李钥匙列表。 有以下属性可以实现这一点。 使用spring.sleuth.baggage-keys,则设置前缀为baggage-对于 HTTP 调用和baggage_用于消息传递。 您还可以使用spring.sleuth.propagation-keys属性以传递一个前缀键列表,这些键传播到远程服务,没有任何前缀。 您还可以使用spring.sleuth.local-keys属性来传递列表键,这些键将在本地传播,但不会通过网络传播。 请注意,没有x-在标题键前面。

为了自动将行李值设置为 Slf4j 的 MDC,您必须将 这spring.sleuth.log.slf4j.whitelisted-mdc-keys属性列表为 行李和传播密钥。例如spring.sleuth.log.slf4j.whitelisted-mdc-keys=foo将设置foo行李进入 MDC。spring-doc.cadn.net.cn

请注意,从下一个下游跟踪上下文开始,额外字段将传播并添加到 MDC。立即 在当前跟踪上下文中将额外的字段添加到 MDC,将该字段配置为在更新时刷新:spring-doc.cadn.net.cn

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

如果要将行李条目添加为标签,以便能够通过行李条目搜索跨度,可以将spring.sleuth.propagation.tag.whitelisted-keys以及列入白名单的行李钥匙列表。要禁用该功能,您必须将spring.sleuth.propagation.tag.enabled=false财产。spring-doc.cadn.net.cn

5.1.2. 提取传播的上下文

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

此实用程序用于标准仪器(例如HttpServerHandler),但也可用于自定义 RPC 或消息传递代码。spring-doc.cadn.net.cn

TraceContextOrSamplingFlags通常仅与Tracer.nextSpan(extracted),除非你是 在客户端和服务器之间共享跨度 ID。spring-doc.cadn.net.cn

5.1.3. 在客户端和服务器之间共享跨度 ID

正常的检测模式是创建一个表示 RPC 服务器端的 span。Extractor.extract应用于传入客户端请求时,可能会返回完整的跟踪上下文。Tracer.joinSpan尝试继续此跟踪,使用相同的跨度 ID(如果支持)或创建子跨度 如果没有。共享跨度 ID 时,报告的数据会包含一个标志,说明这一点。spring-doc.cadn.net.cn

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

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

某些传播系统仅转发父跨度 ID,在Propagation.Factory.supportsJoin() == false. 在这种情况下,始终会预配新的跨度 ID,传入上下文确定父 ID。spring-doc.cadn.net.cn

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

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

注意:某些跨度报告器不支持共享跨度 ID。 例如,如果您将Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive),您应该通过设置Tracing.Builder.supportsJoin(false). 这样做会强制新的子跨度Tracer.joinSpan().spring-doc.cadn.net.cn

5.1.4. 实现传播

TraceContext.Extractor<C>Propagation.Factory插件。 在内部,此代码创建联合类型TraceContextOrSamplingFlags,并具有以下选项之一:spring-doc.cadn.net.cn

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