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中新增的,其概念可以理解为一个线程,其类型有跨进程和跨线程。

 
 
3、span:是链路中数据收集的最小单位,可以理解为某一个方法。span的分类为EntrySpan、LocalSpan及ExitSpan。
EntrySpan 代表服务提供者, 也是服务器端的端点. 作为一个 APM 系统, 我们的目标是应用服务器. 所以几乎所有的服务和 MQ-消费者 都是 EntrySpan.
 
LocalSpan 表示普通的 Java 方法, 它与远程服务无关, 不是 MQ 生产者/消费者, 也不是服务(例如 HTTP 服务)提供者/消费者.
 
ExitSpan 代表一个服务客户端或 MQ 的生产者, 在 SkyWalking 的早期命名为 LeafSpan. 例如 通过 JDBC 访问 DB, 读取 Redis/Memcached 被归类为 ExitSpan.
 
 
三、上下文载体ContextCarrier

为了实现分布式追踪, 需要绑定跨进程的追踪, 并且上下文应该在整个过程中随之传播. 这就是 ContextCarrier 的职责.

以下是有关如何在 A -> B 分布式调用中使用 ContextCarrier 的步骤.

  1. 在客户端, 创建一个新的空的 ContextCarrier.
  2. 通过 ContextManager#createExitSpan 创建一个 ExitSpan 或者使用 ContextManager#inject 来初始化 ContextCarrier.
  3. 将 ContextCarrier 所有信息放到请求头 (如 HTTP HEAD), 附件(如 Dubbo RPC 框架), 或者消息 (如 Kafka) 中
  4. 通过服务调用, 将 ContextCarrier 传递到服务端.
  5. 在服务端, 在对应组件的头部, 附件或消息中获取 ContextCarrier 所有内容.
  6. 通过 ContextManager#createEntrySpan 创建 EntrySpan 或者使用 ContextManager#extract 来绑定服务端和客户端.

让我们通过 Apache HttpComponent client 插件和 Tomcat 7 服务器插件演示, 步骤如下:

  1. 客户端 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());
            }

 

  1. 服务端 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 中很常见. 跨进程和跨线程十分相似, 因为都是需要传播上下文. 唯一的区别是, 跨线程不需要序列化.

以下是有关跨线程传播的三个步骤:

  1. 使用 ContextManager#capture 方法获取 ContextSnapshot 对象.
  2. 让子线程以任何方式, 通过方法参数或由现有参数携带来访问 ContextSnapshot
  3. 在子线程中使用 ContextManager#continued

五、上下文管理器 (ContextManager)

ContextManager 提供所有主要 API.

  1. 创建 EntrySpan
public static AbstractSpan createEntrySpan(String endpointName, ContextCarrier carrier)

根据操作名称(例如服务名称, uri) 和 上下文载体 (ContextCarrier) 创建 EntrySpan.

  1. 创建 LocalSpan
public static AbstractSpan createLocalSpan(String endpointName)

根据操作名称(例如完整的方法签名)创建 (e.g. full method signature)

  1. 创建 ExitSpan
public static AbstractSpan createExitSpan(String endpointName, ContextCarrier carrier, String remotePeer)

根据操作名称(例如服务名称, uri), 上下文载体 (ContextCarrier) 以及对等端 (peer) 地址 (例如 ip + port 或 hostname + port) 创建 ExitSpan.

posted @ 2022-03-12 22:18  骑着蜗牛去救你  阅读(3695)  评论(0编辑  收藏  举报