5. 传播
需要传播以确保源自同一根的活动一起收集在同一跟踪中。 最常见的传播方法是通过向接收跟踪上下文的服务器发送 RPC 请求,从客户端复制跟踪上下文。
例如,在进行下游 HTTP 调用时,其跟踪上下文将编码为请求标头并随其一起发送,如下图所示:
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 内置的,并在多种语言和框架中实现。
大多数用户使用框架拦截器来自动传播。 接下来的两个示例显示了它如何用于 Client 端和服务器。
以下示例显示了 Client 端传播的工作原理:
@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);
以下示例显示了服务器端传播的工作原理:
@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,如以下示例所示:
// 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 能够正确共存,请传递其跟踪标头,如以下示例所示:
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-code
和user-id
字段中的x-baggage-country-code
和x-baggage-user-id
分别:
Tracing.newBuilder().propagationFactory(
ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
.addField("x-vcap-request-id")
.addPrefixedFields("x-baggage-", Arrays.asList("country-code", "user-id"))
.build()
);
稍后,您可以调用以下代码来影响当前跟踪上下文的 country 代码:
ExtraFieldPropagation.set("x-country-code", "FO");
String countryCode = ExtraFieldPropagation.get("x-country-code");
或者,如果您有对跟踪上下文的引用,则可以显式使用它,如以下示例所示:
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
列表为 Whitelisted
baggage 和 propagation keys 的 Carrier Keys 的 Carrier Keys 中。例如spring.sleuth.log.slf4j.whitelisted-mdc-keys=foo
将设置foo
行李进入 MDC。
请注意,额外的字段将从下一个下游跟踪上下文开始传播并添加到 MDC 中。立即 将 extra 字段添加到 Current trace 上下文中的 MDC 中,将字段配置为 FLUSH on UPDATE:
@Bean
ScopeDecorator mdcScopeDecorator() {
BaggageField countryCodeField = BaggageField.create("x-country-code");
return MDCScopeDecorator.newBuilder()
.clear()
.add(SingleCorrelationField.newBuilder(countryCodeField)
.flushOnUpdate()
.build())
.build();
}
请记住,向 MDC 添加条目会大大降低应用程序的性能! |
如果要将 Baggage 条目添加为标签,以便可以通过 Baggage 条目搜索 span,则可以将spring.sleuth.propagation.tag.whitelisted-keys
带有列入白名单的行李钥匙列表。要禁用该功能,您必须将spring.sleuth.propagation.tag.enabled=false
财产。
5.1.2. 提取传播的上下文
这TraceContext.Extractor<C>
从传入请求或消息中读取跟踪标识符和采样状态。
运营商通常是请求对象或标头。
此实用程序用于标准插桩(例如HttpServerHandler
),但也可用于自定义 RPC 或消息收发代码。
TraceContextOrSamplingFlags
通常仅用于Tracer.nextSpan(extracted)
,除非您是
在客户端和服务器之间共享 SPAN ID。
5.1.3. 在 Client 和 Server 之间共享 span ID
常规的插桩模式是创建一个表示 RPC 服务器端的 span。Extractor.extract
在应用于传入客户端请求时,可能会返回完整的跟踪上下文。Tracer.joinSpan
尝试使用相同的 SPAN ID(如果支持)继续此跟踪,或创建子 SPAN
如果不是。共享 span ID 时,报告的数据将包含一个标志。
下图显示了 B3 传播的示例:
┌───────────────────┐ ┌───────────────────┐
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,在Propagation.Factory.supportsJoin() == false
.
在这种情况下,始终会预置新的 span ID,并且传入上下文将确定父 ID。
下图显示了 AWS 传播的示例:
┌───────────────────┐ ┌───────────────────┐
x-amzn-trace-id │ TraceContext │ │ TraceContext │
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
│ Root │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
│ │ │ │ │ │ │ │ │ │
│ Parent │─────────┼─┼> SpanId │ │──────┼─┼> ParentSpanId │ │
└───────────────────┘ │ └───────────────┘ │ │ │ │ │
└───────────────────┘ │ │ SpanId: New │ │
│ └───────────────┘ │
└───────────────────┘
注: 某些 span reporter 不支持共享 span ID。
例如,如果您将Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)
,您应该通过设置Tracing.Builder.supportsJoin(false)
.
这样做会强制启用新的 child spanTracer.joinSpan()
.
5.1.4. 实现传播
TraceContext.Extractor<C>
由Propagation.Factory
插件。
在内部,此代码创建了联合类型TraceContextOrSamplingFlags
,替换为以下选项之一:
-
TraceContext
如果存在跟踪 ID 和跨度 ID。 -
TraceIdContext
如果存在跟踪 ID,但不存在跨度 ID。 -
SamplingFlags
如果不存在标识符。
一些Propagation
implementations 携带从提取点(例如,读取传入 Headers)到注入(例如,写入传出 Headers)的额外数据。
例如,它可能带有请求 ID。
当 implementations 有额外的数据时,它们会按如下方式处理它:
-
如果
TraceContext
提取的TraceContext.extra()
. -
否则,请将其添加为
TraceContextOrSamplingFlags.extra()
哪Tracer.nextSpan
处理。