Span
Span在分布式追踪系统中一个重要且常用的概念。可从Google Dapper Paper和 OpenTracing学习更多与Span相关的知识。
Skywalking从2017年开始支持OpenTracing
和OpenTracing API
; Skywalking中Span概念与谷歌Dapper
论文 和 OpenTracing
类似,但是也做了扩展。
Span有三种类型
- Entry Span:代表服务提供者,也是服务端的端点。作为一个APM系统,我们的目标是应用服务器。所以,几乎所有的服务和MQ-消费者都是EntrySpan。
- Local Span:代表普通的Java方法,与远程服务无关。不是MQ生产者/消费者,也不是服务(如 HTTP服务)的提供者/消费者
- Exit Span:代表一个服务的客户端或者MQ的生产者,在Skywaking早期被命名为
LeafSpan
。例如,通过JDBC访问DB 和 读取Redis/Memcached
被归类为ExitSpan
。
上下文载体 ContextCarrier
为了实现分布式跟踪,需要绑定跨进程追踪,并且上下文应该在整个过程中传播。这就是ContextCarrier职责。
以下是有关如何在 A->B 分布式调用中使用 ContextCarrier 的步骤。
- 在客户端创建一个新的空的
ContextCarrier
- 调用
ContextManager#createExitSpan
创建一个ExitSpan
或者 使用ContextManager#inject
初始化ContextCarrier
- 将
ContextCarrier
所有信息放到请求头(如 HTTP HEAD)、附件(如 Dubbo PPC框架)或者消息(如 Kafka)中 ContextCarrier
通过服务调用传递到服务端- 服务端从对应组件的头部、附件、消息中获取 ContextCarrier 所有内容
- 通过
ContextManager#createEntrySpan
创建EntrySpan 或者 使用ContextManager#extract
来绑定服务端和客户端
让我们通过 Apache HTTPComponent client 插件 和 Tomcat7 服务端插件演示,步骤如下:
- 客户端 Apache HTTPComponent client 插件
1
2
3
4
5
6span = ContextManager.createExitSpan("/span/operation/name", contextCarrier, "ip:port");
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
httpRequest.setHeader(next.getHeadKey(), next.getHeadValue());
} - 服务端Tomcat7 服务器插件
1
2
3
4
5
6
7
8ContextCarrier contextCarrier = new ContextCarrier();
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
next.setHeadValue(request.getHeader(next.getHeadKey()));
}
span = ContextManager.createEntrySpan(“/span/operation/name”, contextCarrier);上下文快照ContextSnapshot
除了跨进程,跨线程也是需要支持的。例如,异步进程(内存中的消息队列)和批处理 在Java中很常见。跨进程和跨线程十分相似,因为都是需要传播上下文, 唯一区别是,跨线程不需要序列化。
以下是有关跨线程传播的三个步骤:
- 使用
ContextManager#capture()
方法获取 ContextSnapshot 对象 - 让子线程以任何方式,通过方法参数或由现有参数携带来访问ContextSnapshot
- 在子线程中使用
ContextManager#continued
核心APIS
上下文管理器ContextManager
ContextManager提供所有主要API
创建 EntrySpan
public static AbstractSpan createEntrySpan(String endpointName, ContextCarrier carrier)
根据操作名称(例如 服务名称、uri) 和 上下文载体ContextCarrier
创建EntrySpan创建 LocalSpan
public static AbstractSpan createLocalSpan(String endpointName)
根据操作名称(例如 完整的方法前面)创建LocalSpan创建 ExitSpan
public static AbstractSpan createExitSpan(String endpointName, ContextCarrier carrier, String remotePeer)
根据操作名称(例如 服务名称、uri),上下文载体ContextCarrier
以及对等端(peer)地址(例如 ip+port 或 hostname+port)创建ExitSpan
AbstractSpan
1 | /** |
除了设置操作名称、标签信息和日志之外,还要设置两个属性,即component组件和layer层。 特别是对于 EntrySpan 和 ExitSpan。
SpanLayer是Span的类别,有5个值
- UNKNOWN(默认值)
- DB
- RPC_FRAMEWORK(针对 RPC 框架,非普通的 HTTP 调用)
- HTTP
- MQ
组件 ID 由 SkyWalking 项目定义和保留。对于组件名称/ID 的扩展,请遵循组件库定义和扩展文档
特殊的 Span Tags
所有标签在跟踪视图都是可用的。 同时,在 OAP 后端分析中,一些特殊的标签或标签组合提供了其他高级功能。
Tag key status_code
该值应该是一个整数。 OAL 实体的响应码对应这个值。
Tag keys db.statement
和 db.type
db.statement
的值代表数据库语句,如SQL,如果值为空则为[No statement]/+span#operationName。当ExitSpan包含此标签时,OAP 会根据 agent-analyzer/default/maxSlowSQLLength 对慢SQL进行采样。慢SQL的阈值由agent-analyzer/default/slowDBAccessThreshold
定义
扩展逻辑端点:Tag key x-le
逻辑端点是一个概念,不代表真正的 RPC 调用,但需要统计信息。 x-le 的值应该是 JSON 格式。 有两种选择:
- 定义一个分离的逻辑端点。 提供其自己的端点名称、延迟和状态。 适用于EntrySpan 和 LocalSpan。
1
2
3
4
5{
"name": "GraphQL-service",
"latency": 100,
"status": true
} - 声明代表逻辑端点的当前本地跨度。
1 | { |
高级API
异步 Span API
关于Span有一系列的高级API,他们都是在异步场景下使用的。 当Span的Tag,日志和属性(包括结束时间)需要在另一个线程中设置时,你就应该使用这些API。
1 | /** |
- 在原始上下文中调用
#prepareForAsync
- 将Span传递到其他线程
- 在全部操作就绪之后,可在任意线程中调用``#asyncFinish` 结束调用。
- 当所有Span的
#prepareForAsync
完成后,跟踪上下文会结束,并一起被回传到后端服务(根据API执行次数判断)
扫描二维码,分享此文章