生日今朝是,匆匆又一年。
欢迎关注【架构染色】交流学习
一、需求背景:
随着自研的 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 埋点的功能,需要通过显示的编码来实现以下功能:
-
EntrySpan
-
如何创建 EntrySpan,指定 Span 名称 -
如何设置 ref 关联上游服务的 ExitSpan ,即执行 Extract(Build the reference between this segment and a cross-process segment) -
如何打 Tag -
如何写 Log -
如何关闭 Span -
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();
所以,根据示例代码应该这样分三步写:
-
构建 carrier -
extract
carrier,得到一个spanContext
-
传入 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#extract
中 get()
为 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