因为用到了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的表现如下图:
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界面上放在一起。如下图:
- 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…
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程序分布式监控方案提供了很大的便利性。