SkyWalking 的Trace还能通过编码构建 -上篇

SkyWalking 的Trace还能通过编码构建 -上篇

生日今朝是,匆匆又一年

欢迎关注【架构染色】交流学习

一、需求背景:

随着自研的 Native 端 SkyWalking Agent 落地,其能力、稳定性逐渐被肯定,隔壁移动部门的核心 WZ 业务开启了对接,链路信息覆盖了界面切换、用户操作以及请求交互,实时、精准的 Trace 数据让同事的日常巡检和问题排查变的轻松许多,用的爽了想法就多了,这不正准备将追踪能力应用到 IM 系统上,以便加强对 IM 数据的检测,提升 IM 问题的响应速度。

在去年有做过 IM 服务端的插件,但由于历史原因,服务端的线程特别多,一个请求就要二十多个 Segment。当时 SkyWalking 系统还未调优,IM 的数据量太大,另外服务端也有重构的规划,多方因素综合考量后未能上线。这次需配合需求把链路补齐。考虑到即使定制了插件也无通用性、系统自身又要升级迭代、插件的调试更新比较麻烦等几个关键因素,系统负责人更期望通过显式的调用 API 用编码的方式构建 Trace,以满足其快速实现、快速迭代的需求。

二、寻找方案

印象之中,之前在整理 SkyWalking 的插件套件的时,似乎skywalking-agentactivations中有一个插件具有这个能力,翻看之后,找到了apm-toolkit-opentracing-activation,从文档中找到如下简单的介绍:

  • Dependency the toolkit, such as using maven or gradle
   <dependency>
      <groupId>org.apache.skywalking</groupId>
      <artifactId>apm-toolkit-opentracing</artifactId>
      <version>{project.release.version}</version>
   </dependency>
  • Use our OpenTracing tracer implementation
Tracer tracer = new SkywalkingTracer();
Tracer.SpanBuilder spanBuilder = tracer.buildSpan("/yourApplication/yourService");

内容有点少,再从百度翻查了几页,也只是看到简单的能力描述 “通过代码方式对 trace 信息进行补充”,而在功能详解和使用示例方面则没有收获。

不过有源码就不担心,因为 SkyWalking 的源码中给的注释还是很清晰的,于是开始探索验证。

三、所需能力梳理

满足 IM 埋点的功能,需要通过显示的编码来实现以下功能:

  1. EntrySpan

    • 如何创建 EntrySpan,指定 Span 名称
    • 如何设置 ref 关联上游服务的 ExitSpan ,即执行 Extract(Build the reference between this segment and a cross-process segment)
    • 如何打 Tag
    • 如何写 Log
    • 如何关闭 Span
  2. ExitSpan

    • 如何创建 ExitSpan
    • 如何指定 Peer
    • 如何打 Tag
    • 如何写 Log
    • 如何关闭 Span
    • 如何通过 Inject 构建 跨进程 信息,传递给下游服务完成 ref 的关联

先给结论:考虑到内容太长,影响阅读体验,就分成上下两篇来介绍,为了满足一些同学快速使用这个能力的需求,下文先给结论,再补充摸索过程中遇到的问题。下面开始介绍如何创建能关联上游服务的 EntrySpan

四、编码方式创建EntrySpan

4.1 添加依赖

具体的版本请根据自家的情况填写

<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-opentracing</artifactId>
    <version>xxx</version>
</dependency>

4.2 一个有效的示例

private static void makeEntryTrace(){
    Tracer tracer = new SkywalkingTracer();

    //1. 构建EntrySpan
    ActiveSpan activeSpan = tracer.buildSpan("/ReceiveMessage")
            .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)// 注意必须设置为entrySpan,才算是EntrySpan
            .startActive();

    //2. 构建 Carrier 并 通过 extract 注入到 当前Segment 的 ref
    //注意 注意 注意 这个代码必须要 构建EntrySpan之后。
    Map map = new HashMap();
    String sw8 = "1-OTUxNjNlODUxNGVjNGJmZDgwZDI2MmNmZjZiYzNiZWEuMTAzLjE2Njk2MjYxMzc4MTQwMTEx-OTUxNjNlODUxNGVjNGJmZDgwZDI2MmNmZjZiYzNiZWEuMTAzLjE2Njk2MjYxMzc4MTQwMDAw-1-c2t5d2Fsa2luZy1kZW1v-MTI3LjAuMC4x-L2R1YmJv-MTI3LjAuMC4xOjIwODgw";
    map.put("sw8",sw8);//先只构建一个sw8,其他两个暂不处理
    TextMap headerCarrier = new TextMapExtractAdapter(map);
    tracer.extract(
            Format.Builtin.TEXT_MAP,
            headerCarrier);

    //设置标签
    activeSpan.setTag("a","a");
    activeSpan.setTag("b","b");
    activeSpan.setTag("c","c");

    //当前EntrySpan中写日志
    activeSpan.log("log 1");

    //给当前EntrySpan指定名称,有些情况需要这种在运行到一定阶段后,才能从某个变量中获得值,用于构建span的名称
    activeSpan.setOperationName("/ReceiveMessage");//重新指定名称

    //关闭span,因为只有一个EntrySpan,则会关闭整个Segment,上报给OAP
    activeSpan.deactivate();
    System.out.println("成功关闭EntrySpan,Segment已上报可通过 TID : 95163e8514ec4bfd80d262cff6bc3bea.103.16696261378140111 查询");
}

如果就是为了找个示例使用参考,那么看到这里就可以了,因一些不可抗拒的原因无法截图,所以没提供。接下来的内容是介绍为什么示例代码会是上边的样子,笔者在摸索验证过程中遇到了哪些问题,关于如何调试 SkyWalking Agent 本篇先不介绍,后续再补充。

五、摸索时遇到的问题

5.1 Sample values 只是示例,可不能用

1)错误示范

官网找个sw8的 Sample values,于是代码如下:

String sw8 = "1-TRACEID-SEGMENTID-3-PARENT_SERVICE-PARENT_INSTANCE-PARENT_ENDPOINT-IPPORT"
map.put("sw8","xxx");//先只构建一个sw8,其他两个暂不处理

2)错误原因

org.apache.skywalking.apm.agent.core.context.ContextManager#extract中校验出给的值不合法。

public static void extract(ContextCarrier carrier) {
    if (carrier == null) {
        throw new IllegalArgumentException("ContextCarrier can't be null.");
    }
    //注意Sample values,这里校验不通过。
    if (carrier.isValid()) {
        get().extract(carrier);
    }
}

3)老老实实找个正确的值,这里提供一个示例

String sw8 = "1-OTUxNjNlODUxNGVjNGJmZDgwZDI2MmNmZjZiYzNiZWEuMTAzLjE2Njk2MjYxMzc4MTQwMTEx-OTUxNjNlODUxNGVjNGJmZDgwZDI2MmNmZjZiYzNiZWEuMTAzLjE2Njk2MjYxMzc4MTQwMDAw-1-c2t5d2Fsa2luZy1kZW1v-MTI3LjAuMC4x-L2R1YmJv-MTI3LjAuMC4xOjIwODgw";

5.2 extract 的 Example 开了个玩笑

1)错误示范

io.opentracing.Tracer#extract的注释给出 Example, 指引如下

Example:
       Tracer tracer = ...
       TextMap httpHeadersCarrier = new AnHttpHeaderCarrier(httpRequest);
       SpanContext spanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, httpHeadersCarrier);
       ... = tracer.buildSpan('...').asChildOf(spanCtx).startActive();

所以,根据示例代码应该这样分三步写:

  1. 构建 carrier
  2. extract carrier,得到一个 spanContext
  3. 传入 spanContext 创建 span

错误示范代码如下

//1. 构建 Carrier
Map map = new HashMap();
map.put("sw8","xxx");//先只构建一个sw8,其他两个暂不处理
TextMap headerCarrier = new TextMapExtractAdapter(map);
//2. 并 通过 extract 注入到 当前Segment 的 ref
SpanContext extract = tracer.extract(
        Format.Builtin.TEXT_MAP,
        headerCarrier);

//3. 构建EntrySpan
ActiveSpan activeSpan = tracer.buildSpan("/ReceiveMessage")
        .asChildOf(extract)
        .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)// 注意必须设置为entrySpan,才算是EntrySpan
        .startActive();

2)错误原因

org.apache.skywalking.apm.agent.core.context.ContextManager#extractget()为 null。

public static void extract(ContextCarrier carrier) {
    if (carrier == null) {
        throw new IllegalArgumentException("ContextCarrier can't be null.");
    }
    if (carrier.isValid()) {
        // get() 为null
        get().extract(carrier);
    }
}

为啥 get() 为 null,因为org.apache.skywalking.apm.agent.core.context.ContextManager中的CONTEXT还是空的,为啥是空的呢?因为CONTEXT上下文都还没初始化。

private static AbstractTracerContext get() {
    return CONTEXT.get();
}

3)通过创建span,先初始化CONTEXT

//1. 先构建EntrySpan
ActiveSpan activeSpan = tracer.buildSpan("/ReceiveMessage")
        .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)// 注意必须设置为entrySpan,才算是EntrySpan
        .startActive();

其他问题还有一些,毕竟忙活了大半天,但笔者感觉有些问题消耗精力是因为缺少引导资料,这里就不拿出来叨扰大家了。

六、总结

SkyWalking 主打非侵入式的 Agent 探针能力,从网络中没有顺利的找到自主编程式构建 Trace 的资料,还不确定这种方法是不被推荐、不常用或宣传不到位的哪种原因。当然 Java Agent 方式也是笔者很推崇的,因之前经历过基于 Cat 这种侵入式的埋点,当 Agent 能力需要升级时,要推动业务方配合升级是很麻烦的。但本次遇到的服务无通用性且自身有常规迭代的场景中,似乎用户自主通过编程 API 构建 Trace 信息,倒显得更合适。

本篇的内容看似简单,实际需要对 SkyWalking Agent 的工作机制、Trace的数据模型、源码以及调试技巧等较为熟悉,否则遇到这些问题会无从下手,后边笔者会陆续基于 SkyWalking 整理出更多 APM 相关的文章,还望专家老师不吝赐教。

最后说一句(请关注,莫错过)

如果这篇文章对您有帮助,或有所启发的话,欢迎关注公众号【 架构染色 】进行交流和学习。您的支持是我坚持写作最大的动力。

原文始发于微信公众号(架构染色):SkyWalking 的Trace还能通过编码构建 -上篇

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/65454.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!