传播需要确保来自同一根的活动被收集到同一个跟踪中。最常见的传播方法是通过向接收跟踪上下文的服务器发送 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传播,它内置于 Brave 中,并且在许多语言和框架中都有实现。 大多数用户使用框架拦截器来自动传播。接下来的两个例子展示了这对于客户端和服务器是如何工作的。 下面的示例展示客户端传播可能如何工作: @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)); 有时需要传播额外的字段,例如请求 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 服务环境中,但没有向 X-Ray 报告数据。为了确保 X-Ray 能够正确共存,请通过其跟踪头,如下例所示: tracingBuilder.propagationFactory(
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-amzn-trace-id")
);
如果它们遵循一个共同的模式,你也可以为字段加前缀。下面的示例展示如何按原样传播 Tracing.newBuilder().propagationFactory( ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY) .addField("x-vcap-request-id") .addPrefixedFields("x-baggage-", Arrays.asList("country-code", "user-id")) .build() ); 稍后,你可以调用以下代码以影响当前跟踪上下文的 country code: 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");
为了自动将 baggage 值设置为 Slf4j 的 MDC,必须使用白名单 baggage 和 传播 key 设置
如果要将 baggage 条目添加为标签,为了能够通过 baggage 条目搜索 Span ,可以使用白名单 baggage key 设置
此实用程序用于标准检测(如
正常的检测模式是创建一个表示 RPC 服务器端的 Span 。 下图展示了 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,在 下图展示了 AWS 传播的示例: ┌───────────────────┐ ┌───────────────────┐ x-amzn-trace-id │ TraceContext │ │ TraceContext │ ┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │ │ Root │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │ │ │ │ │ │ │ │ │ │ │ │ Parent │─────────┼─┼> SpanId │ │──────┼─┼> ParentSpanId │ │ └───────────────────┘ │ └───────────────┘ │ │ │ │ │ └───────────────────┘ │ │ SpanId: New │ │ │ └───────────────┘ │ └───────────────────┘
注意:一些 Span 报告程序不支持共享 Span ID。例如,如果设置
一些传播实现将额外的数据从提取点(例如,读取传入头)传输到注入点(例如,写入传出头)。例如,它可能携带一个请求 ID。当实现有额外的数据时,它们会按如下方式处理它:
|