谈谈Skywalking架构和设计

因为用到了skywalking,用的过程特别顺利,心里对这个是非常的欣赏。所以特别来写一写Skywalking架构和设计。

我之前有过一些类似的经验

  • 写过一个rpc服务的skywalking插件
  • 研读过很多bytebuddy的例子,比如Mockito
  • 用过jaeger等链路追踪服务

但并没有做一个全面的梳理。本文将尽量全面的谈谈Skywalking架构和设计。

从没有用过skywalking的同学,建议先查看使用指南。

Skywalking目标

Skywalking目标是全链路的分布式的解决方案。

Skywalking概念和原理

skywalking也是基于OpenTracing协议的。

OpenTracing协议

OpenTracing是分布式跟踪协议,当我们把系统拆成服务化,分布式系统的时候,查询一个问题,很可能需要登录多台机器。 OpenTracing通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。

但是,Opentracing也不是一个标准,只是需要开发人员将一些Instrumentation加入到代码程序里面。

Span

要理解分布式的调用链的设计,有一个概念是必不可少的,那就是Span。Span是具有操作名称、时长的一个运行单元。

  • 每个span都可以有各自的tags
  • logs
  • 还有有父子关系

进程内

在同一个进程内,Span的表现如下图:

span.png

parent Span包含了child Span的时长。但是各自的tags、logs是相互独立的。

跨越进程

当需要跨越进程进行传递的时候我没需要使用到SpanContexts。
然而跨进程调用的方式有很多,比如http/grpc/kafka等。

为了抽象出统一的概念,OpenTracing提出了Tracer的API(io.opentracing.Tracer)通过carrier去操作spanContext

skywalking的carrier处理

  • 将请求带上contextCarrier
final ContextCarrier contextCarrier = new ContextCarrier();
String remotePeer = meta.getCallInfo().getCallee();
AbstractSpan span = ContextManager.createExitSpan(invocation.getFunc(), contextCarrier, remotePeer);

...
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
    next = next.next();
    request.getAttachments().put(next.getHeadKey(), next.getHeadValue());
}

复制代码

这段代码将ContextCarrier作为Attachments塞给请求传递给被调用方。

  • 被调用方将请求的getAttachment设置给CarrierItem
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
    next = next.next();
    String headKey = next.getHeadKey();
    next.setHeadValue(new String((byte[]) request.getAttachment(headKey)));
}
复制代码

这段代码Attachments放到ContextCarrier。这样就保证调用方和被调用方有相同的carrier,被连接在一起。如下图,他们就会在UI界面上放在一起。如下图:

span2.png

  • span3调用服务端的服务
  • 蓝色的服务端处理,span4变成了span3的child。

几种不同类型的span

在编程的时候,要注意这几个概念。

  • ExitSpan:一般用于客户端
  • EntrySpan:一般用于服务起点
  • LocalSpan:local的相关调用

利器:无侵入的字节码增强

不止是skywalking,像其他很多框架也是利用无侵入的字节码增强技术,这样的做法给我们提供了最便利的使用体验。

字节码增强的原理

  • javaagent Instrument

JVMTI可以支持第三方工具程序以代理的方式连接和访问JVM,并利用JVMTI提供的丰富的编程接口,完成很多跟JVM相关的功能;
在JVM启动时,通过JVM参数-javaagent,传入agent jar,Instrument Agent被加载
也可以在JVM启动后,attach agent包

skywalking的工作方式就是在在JVM启动时,通过JVM参数-javaagent。

public static void premain(String agentArgs, Instrumentation inst) {
  //sk的Transformer做的很有扩展性,支持插件机制。
    inst.addTransformer(new MyClassFileTransformer());
}
复制代码

字节码框架

  • ASM是一种字节码框架,非常难使用
  • Bytebuddy也字节码框架,使用方便

可以利用字节码框架修改字节码,在通过JVM TI和javaagent技术完成类的替换。

追踪实现的原理

skywalking agent为了能够让更多开发者加入开发,并且能够有可扩展性,使用了插件机制。 agent启动时会加载所有plugins,进行字节码增强。

Plugins的核心问题有2个:

(1)创建span,让它能够显示Trace调用链 

(2)考虑如何传输,例如Kafka需要考虑如何把它加入kafka header中;HTTP需要考虑加入Http Header中。

skywalking整体架构

官网源码: github.com/apache/skyw…

image.png
Skywalking目前想要做成跟踪、监控、日志一体的解决方案(Tracing, Metrics and Logging all-in-one solution)。

功能分解

从整体架构图可以看出。Skywalking功能明晰、解耦性强。

  • 数据收集:Tracing依赖探针(Agent),Metrics依赖Prometheus或者新版的Open Telemetry,日志通过ES或者Fluentd。
  • 数据传输:通过kafka、Grpc、HTTP传输到Skywalking Reveiver
  • 数据解析和分析:OAP系统进行数据解析和分析。
  • 数据存储:后端接口支持多种存储实现,例如ES。
  • UI模块:通过GraphQL进行查询,然后通过VUE搭建的前端进行展示。
  • 告警:可以对接多种告警,最新版已经支持钉钉。

微服务想要使用监控追踪,只要加上对应的agent就可以了。

其他

采样率

skywalking agent调整采样率,减少数据上传

通过agent.sample_n_per_3_secs设置3秒内采样的数量,一般500~2000是合适的值。默认-1全采样。 在设置agent采样率后,如果调用链上游进行了采样,那么下游会忽略采样率进行强制采样,保证Trace调用链完整

性能

性能损耗控制 由于操作的是生产环境,不能对现有代码产生严重影响,所以需要控制性能损耗。

  • 相比于侵入性地编写log打印,skywalking的性能剖析不需要埋点,也就不会增加额外的日志打印开销。
  • 异步批量的传送日志。

总结

本文简单讲了Skywalking架构和设计。总的来说,Skywalking架构简单、利用无侵入的字节码增强技术使用很方便,扩展性强,这对Java程序分布式监控方案提供了很大的便利性。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享