skywalking之trace、tracesegment和span以及ContextCarrier和ContextSnapshot
一、SkyWalking是一款 分布式追踪系统,随着微服务架构的流行,一些微服务架构下的问题也会越来越突出,比如一个请求会涉及多个服务,而服务本身可能也会依赖其他服务,整个请求路径就构成了一个网状的调用链,而在整个调用链中一旦某个节点发生异常,整个调用链的稳定性就会受到影响。skywalking通过使用字节码增强技术实现对特定方法的监控,并收集数据用于分析。
二、名词解释
注:关于更详细的名词解释,可以查看官网文档 https://skyapm.github.io/document-cn-translation-of-skywalking/
1、trace:指的是整个微服务中的调用链路,通过traceid实现串联。
2、tracesegment:在openTracing规范中没有提到tracesegment,这个概念是在Skywalking中新增的,其概念可以理解为一个线程,其类型有跨进程和跨线程。
LeafSpan
. 例如 通过 JDBC 访问 DB, 读取 Redis/Memcached 被归类为 ExitSpan.为了实现分布式追踪, 需要绑定跨进程的追踪, 并且上下文应该在整个过程中随之传播. 这就是 ContextCarrier 的职责.
以下是有关如何在 A -> B
分布式调用中使用 ContextCarrier 的步骤.
- 在客户端, 创建一个新的空的
ContextCarrier
. - 通过
ContextManager#createExitSpan
创建一个 ExitSpan 或者使用ContextManager#inject
来初始化ContextCarrier
. - 将
ContextCarrier
所有信息放到请求头 (如 HTTP HEAD), 附件(如 Dubbo RPC 框架), 或者消息 (如 Kafka) 中 - 通过服务调用, 将
ContextCarrier
传递到服务端. - 在服务端, 在对应组件的头部, 附件或消息中获取
ContextCarrier
所有内容. - 通过
ContextManager#createEntrySpan
创建 EntrySpan 或者使用ContextManager#extract
来绑定服务端和客户端.
让我们通过 Apache HttpComponent client 插件和 Tomcat 7 服务器插件演示, 步骤如下:
- 客户端 Apache HttpComponent client 插件
span = ContextManager.createExitSpan("/span/operation/name", contextCarrier, "ip:port"); CarrierItem next = contextCarrier.items(); while (next.hasNext()) { next = next.next(); httpRequest.setHeader(next.getHeadKey(), next.getHeadValue()); }
- 服务端 Tomcat 7 服务器插件
ContextCarrier 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
五、上下文管理器 (ContextManager)
ContextManager 提供所有主要 API.
- 创建 EntrySpan
public static AbstractSpan createEntrySpan(String endpointName, ContextCarrier carrier)
根据操作名称(例如服务名称, uri) 和 上下文载体 (ContextCarrier) 创建 EntrySpan.
- 创建 LocalSpan
public static AbstractSpan createLocalSpan(String endpointName)
根据操作名称(例如完整的方法签名)创建 (e.g. full method signature)
- 创建 ExitSpan
public static AbstractSpan createExitSpan(String endpointName, ContextCarrier carrier, String remotePeer)
根据操作名称(例如服务名称, uri), 上下文载体 (ContextCarrier) 以及对等端 (peer) 地址 (例如 ip + port 或 hostname + port) 创建 ExitSpan.