52. 传播
需要进行传播以确保源自同一根的活动在同一迹线中收集在一起。最常见的传播方法是通过向接收它的服务器发送 RPC 请求来从 client 复制 trace context。
例如,当进行下游 HTTP 调用时,其 trace context 被编码为 request headers 并随之一起发送,如下图所示:
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 传播,它是 built-in 到 Brave,并且在许多语言和框架中都有 implementations。
大多数用户使用 framework 拦截器来自动传播。接下来的两个示例显示了如何对 client 和服务器起作用。
以下 example 显示了 client-side 传播如何工作:
@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);
以下 example 显示了 server-side 传播如何工作:
@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));
52.1 传播额外的字段
有时您需要传播额外的字段,例如请求 ID 或备用跟踪 context。例如,如果您处于 Cloud Foundry 环境中,则可能需要传递请求 ID,如以下 example 所示:
// 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");
您可能还需要传播未使用的跟踪 context。例如,您可能位于 Amazon Web Services 环境中,但不会将数据报告给 X-Ray。要确保 X-Ray 能正确 co-exist,pass-through 其跟踪标头,如下面的示例所示:
tracingBuilder.propagationFactory(
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-amzn-trace-id")
);
在 Spring Cloud Sleuth 中,跟踪构建器
Tracing.newBuilder()
的所有元素都定义为 beans。所以如果你想传递一个自定义的PropagationFactory
,你就可以创建一个这种类型的 bean,我们将它设置在Tracing
bean 中。
52.1.1 前缀字段
如果它们遵循 common pattern,您也可以为字段添加前缀。以下 example 显示如何传播x-vcap-request-id
字段 as-is,但分别将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()
);
稍后,您可以调用以下 code 来影响当前 trace context 的国家 code:
ExtraFieldPropagation.set("x-country-code", "FO");
String countryCode = ExtraFieldPropagation.get("x-country-code");
或者,如果您对 trace context 有一个 reference,则可以显式使用它,如下面的 example 所示:
ExtraFieldPropagation.set(span.context(), "x-country-code", "FO");
String countryCode = ExtraFieldPropagation.get(span.context(), "x-country-code");
与以前版本的 Sleuth 不同的是,使用 Brave,您必须传递 baggage 键列表。有两个 properties 来实现这一目标。使用
spring.sleuth.baggage-keys
,您可以为 HTTP calls 设置以baggage-
为前缀的键,为消息传递设置baggage_
。您还可以使用spring.sleuth.propagation-keys
property 传递列入白名单但没有任何前缀的前缀键列表。请注意,标题键前面没有x-
。
52.1.2 提取传播的 Context
TraceContext.Extractor<C>
从传入的请求或消息中读取跟踪标识符和采样状态。运营商通常是请求 object 或 headers。
此实用程序用于标准检测(例如`HttpServerHandler``),但也可用于自定义 RPC 或消息传递 code。
TraceContextOrSamplingFlags
通常仅与Tracer.nextSpan(extracted)
一起使用,除非您在 client 和服务器之间共享 span ID。
52.1.3 在 Client 和 Server 之间共享 span ID
正常的检测 pattern 是创建一个表示 RPC 服务器端的 span。当应用于传入的 client 请求时,Extractor.extract
可能会 return 一个完整的 trace context。 Tracer.joinSpan
尝试继续此跟踪,如果支持则使用相同的 span ID,否则创建子 span。当共享 span ID 时,报告的数据包含 flag 这样说。
下图显示了 B3 传播的示例:
┌───────────────────┐ ┌───────────────────┐
Incoming Headers │ TraceContext │ │ TraceContext │
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
│ X─B3-TraceId │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
│ │ │ │ │ │ │ │ │ │
│ X─B3-ParentSpanId │─────────┼─┼> ParentSpanId │ │──────┼─┼> ParentSpanId │ │
│ │ │ │ │ │ │ │ │ │
│ X─B3-SpanId │─────────┼─┼> SpanId │ │──────┼─┼> SpanId │ │
└───────────────────┘ │ │ │ │ │ │ │ │
│ │ │ │ │ │ Shared: true │ │
│ └───────────────┘ │ │ └───────────────┘ │
└───────────────────┘ └───────────────────┘
某些传播系统仅转发 parent span ID,在Propagation.Factory.supportsJoin() == false
时检测到。在这种情况下,始终配置新的 span ID,并且传入的 context 确定 parent ID。
下图显示了 AWS 传播的一个示例:
┌───────────────────┐ ┌───────────────────┐
x-amzn-trace-id │ TraceContext │ │ TraceContext │
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
│ Root │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
│ │ │ │ │ │ │ │ │ │
│ Parent │─────────┼─┼> SpanId │ │──────┼─┼> ParentSpanId │ │
└───────────────────┘ │ └───────────────┘ │ │ │ │ │
└───────────────────┘ │ │ SpanId: New │ │
│ └───────────────┘ │
└───────────────────┘
注意:某些 span 记者不支持共享 span ID。例如,如果设置Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)
,则应通过设置Tracing.Builder.supportsJoin(false)
来禁用连接。这样做会强制Tracer.joinSpan()
上的新子 span。
52.1.4 实施传播
TraceContext.Extractor<C>
由Propagation.Factory
插件实现。在内部,此 code 使用以下之一创建 union 类型TraceContextOrSamplingFlags
:* TraceContext
如果存在 trace 和 span ID。 * TraceIdContext
如果存在跟踪 ID 但 span ID 不存在。 * SamplingFlags
如果没有标识符。
一些Propagation
implementations 从提取点(例如,读取传入的 headers)到注入(例如,编写传出的 headers)携带额外的数据。例如,它可能带有请求 ID。当 implementations 有额外数据时,它们按如下方式处理:*如果提取TraceContext
,则将额外数据添加为TraceContext.extra()
。 *否则,将其添加为TraceContextOrSamplingFlags.extra()
,其中Tracer.nextSpan
处理。