链路追踪

编辑

链路追踪是一组具有共同根的事务Span。每个链路追踪都跟踪单个请求的完整过程。它描述了由单个逻辑操作引起的各个操作及其因果关系。

分布式链路追踪
编辑

当链路追踪像微服务架构中常见的那样穿过多个服务时,它被称为分布式链路追踪。分布式链路追踪包含跨多个分布式组件的操作,跨越进程、网络和安全边界。

Diagram illustrating the relationship between spans
为什么分布式链路追踪很重要?编辑

分布式链路追踪使您能够通过跟踪请求的整个过程来分析整个微服务架构的性能 — 从前端服务上的初始 Web 请求一直到后端服务上的数据库查询。

跟踪请求在服务中传播的情况,可以提供应用程序花费时间、发生错误以及形成瓶颈的端到端视图。分布式链路追踪消除了各个服务的数据孤岛,并揭示了服务边界之外发生的事情。

对于受支持的技术,分布式链路追踪是开箱即用的,无需额外配置。

分布式链路追踪如何工作编辑

分布式链路追踪的工作原理是将自定义的 traceparent HTTP 标头注入到传出的请求中。此标头包含诸如 trace-id 之类的信息,用于标识当前链路追踪,以及 parent-id,用于标识传入请求上的当前 Span 的父级或传出请求上的当前 Span。

当服务处理请求时,它会检查是否存在此 HTTP 标头。如果缺少,则服务会启动新的链路追踪。如果存在,则服务会确保将当前操作添加为现有链路追踪的子级,并继续传播该链路追踪。

链路追踪传播示例编辑

在此示例中,Elastic 的 Ruby 代理与 Elastic 的 Java 代理通信。两者都支持 traceparent 标头,并且链路追踪数据已成功传播。

How traceparent propagation works

在此示例中,Elastic 的 Ruby 代理与 OpenTelemetry 的 Java 代理通信。两者都支持 traceparent 标头,并且链路追踪数据已成功传播。

How traceparent propagation works

在此示例中,链路追踪遇到一段不传播 traceparent 标头的中间件。分布式链路追踪结束,任何进一步的通信都将导致新的链路追踪。

How traceparent propagation works
W3C 链路上下文规范编辑

所有 Elastic 代理现在都支持官方的 W3C 链路上下文规范和 traceparent 标头。有关最低要求的代理版本,请参见下表

代理名称 代理版本

Go 代理

1.6

Java 代理

1.14

.NET 代理

1.3

Node.js 代理

3.4

PHP 代理

1.0

Python 代理

5.4

Ruby 代理

3.5

RUM 代理

5.0

较旧的 Elastic 代理使用唯一的 elastic-apm-traceparent 标头。出于向后兼容性的目的,新版本的 Elastic 代理仍然支持此标头。

可视化分布式链路追踪编辑

应用程序 UI 的时间轴可视化提供了对每个应用程序链路追踪的可视化深入分析

Distributed tracing in the Applications UI
手动分布式链路追踪编辑

对于受支持的技术,Elastic 代理会自动传播分布式链路追踪上下文。如果您的服务通过不同的、不受支持的协议进行通信,您可以使用每个代理的 API 手动将分布式链路追踪上下文从发送服务传播到接收服务。

traceparent 标头添加到传出请求编辑

发送服务必须将 traceparent 标头添加到传出请求。

  1. 使用 startTransaction 启动事务,或使用 startSpan 启动 Span。
  2. 使用 injectTraceHeaderstraceparent 标头注入到请求对象中

手动检测 RPC 框架的示例

// Hook into a callback provided by the RPC framework that is called on outgoing requests
public Response onOutgoingRequest(Request request) throws Exception {
  Span span = ElasticApm.currentSpan() 
          .startSpan("external", "http", null)
          .setName(request.getMethod() + " " + request.getHost());
  try (final Scope scope = transaction.activate()) {
      span.injectTraceHeaders((name, value) -> request.addHeader(name, value)); 
      return request.execute();
  } catch (Exception e) {
      span.captureException(e);
      throw e;
  } finally {
      span.end(); 
  }
}

创建一个表示外部调用的 Span

traceparent 标头注入到请求对象中

结束 Span

解析传入请求的 traceparent 标头编辑

接收服务必须解析传入的 traceparent 标头,并启动新的事务或 Span 作为接收到的上下文的子级。

  1. 使用 startTransactionWithRemoteParent() 创建事务作为传入事务的子级。
  2. 使用 activate()setName() 启动并命名事务。

示例

// Hook into a callback provided by the framework that is called on incoming requests
public Response onIncomingRequest(Request request) throws Exception {
    // creates a transaction representing the server-side handling of the request
    Transaction transaction = ElasticApm.startTransactionWithRemoteParent(request::getHeader, request::getHeaders); 
    try (final Scope scope = transaction.activate()) { 
        String name = "a useful name like ClassName#methodName where the request is handled";
        transaction.setName(name); 
        transaction.setType(Transaction.TYPE_REQUEST); 
        return request.handle();
    } catch (Exception e) {
        transaction.captureException(e);
        throw e;
    } finally {
        transaction.end(); 
    }
}

创建事务作为远程父级的子级

激活事务

命名事务

添加事务类型

最终,结束事务

使用 RUM 的分布式追踪编辑

可能需要一些额外的设置才能将请求与 Real User Monitoring (RUM) 代理正确关联。

请参阅 RUM 分布式追踪指南,了解有关启用跨域请求、设置服务器配置以及使用动态生成的 HTML 的信息。