如之前的文章所介绍,Skywalking主要由Agent、OAP、Storage、UI四大模块组成(如下图):
Agent和业务程序运行在一起,采集链路及其它数据,通过gRPC发送给OAP(部分Agent采用http+json的方式);OAP还原链路(图中的Tracing),并分析产生一些指标(图中的Metric),最终存储到Storage中。本文从源码角度来串联一下这整个流程(基于目前最新的Skywalking 8.0.1)。
源码编译Skywalking
本地调试必须先从源码编译Skywalking,有两种方式,一种是从GitHub拉取代码,一种是从Apache Skywalking的release页面下载代码。区别在于GitHub上面的代码是使用git module管理的,拉取下来需要执行一系列操作,最主要的是没有科学上网的话,速度比较慢。Release页面下载的是已经把依赖关系全部整理好的代码,整个源码包不到3MB,还有很多国内镜像地址,所以下载非常快。两种我都使用过,我的建议是:如果你想看历史提交记录或者想持续跟上游版本的话,就选用从GitHub拉取代码的方式;如果你想方便或者从GitHub clone超级慢的话,建议直接从Release处下载。不管哪种,编译以及导入IDEA或Eclipse官方文档写的都比较详细,我就不做翻译了,基本都是命令操作,英文不好也看得懂(just copy-and-paste~~):How to build.
源码编译成功以后(务必保证编译成功),就可以准备进行调试了。
源码流程简析及调试
这里通过一个简单的Spring MVC程序来演示如何调试Agent和OAP。
创建一个Spring MVC程序
在Skywalking项目下增加一个简单的Spring MVC模块(注意这里一定要以Skywalking项目module的方式添加),这里我创建了一个名叫simple-springmvc的module,增加了一个简单的Controller:/hello/{name}
。如下图:
然后在这个这个MVC程序的VM option中增加如下配置:
-javaagent:{源码根目录}/skywalking-agent/skywalking-agent.jar
-Dskywalking.agent.service_name=simple-springmvc
注意,-javaagent后面那个skywalking-agent.jar路径换成你自己的路径。源码如果编译成功的话,源码根目录下面会出现这个skywalking-agent目录,并且里面会有这个skywalking-agent.jar。如下图:
这样调试用的示例程序以及Skywalking的agent注入也配置好了。先别启动,接下来还需要启动OAP。
启动OAP
如果想先只调试agent的话,可以单独下载一个Skywalking的二进制(编译完以后,根目录下的dist目录也有二进制安装包),本地启动(参考我之前的文章)即可。第一次调试的话,我建议agent和OAP单独调试,因为两者有一些公用代码,在一个工程里面启动的话,容易造成混淆。分开调试的话就本地单独起一个Skywalking就行,这里讲直接在项目里面启动一个OAP的方式。
启动OAP非常简单,OAP的代码是源码根目录下的oap-server,入口函数是org.apache.skywalking.oap.server.starter
包下面的OAPServerStartUp类。直接启动即可。
需要注意的是这样只启动了OAP,为了方便查看还原的链路(不启动也不影响调试,不看Web的直接跳过),我们再手动启动一个Web UI。直接在Skywalking安装目录下面(注意是二进制安装目录,不是源码目录)的webapp目录下执行:java -jar skywalking-webapp.jar
即可。默认访问地址为http://127.0.0.1:8080/。
OAP和UI(optional)启动好以后,就可以开始调试了。
流程简析
启动调试之前,我先简单介绍一下数据流向以及一些关键的函数,方便提前打断点。整个数据流如下图:
这里我们先创建了一个Spring MVC程序simple-springmvc,并且配置了javaagent,这样Skywalking agent就会以字节码注入的方式运行在simple-springmvc里面。当我们使用curl命令发送请求时,就会产生链路数据。需要注意的是,Skywalking默认已经实现了Spring MVC的插件{源码根目录}/skywalking-agent/plugins/apm-springmvc-annotation-commons-8.0.1.jar
,对应的源码是{源码根目录}/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons
。它的增强函数就是在这个模块下的AbstractMethodInterceptor
类中实现的,给这个类的beforeMethod
方法打个断点(为了节省篇幅,省略了一些不重要的代码),就可以观察数据agent增强流程:
package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
public abstract class AbstractMethodInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
// 给下面这行打个断点
Boolean forwardRequestFlag = (Boolean) ContextManager.getRuntimeContext().get(FORWARD_REQUEST_FLAG);
/**
* Spring MVC plugin do nothing if current request is forward request.
* Ref: https://github.com/apache/skywalking/pull/1325
*/
if (forwardRequestFlag != null && forwardRequestFlag) {
return;
}
// 以下省略
}
// 以下省略
}
然后启动了OAP,后端存储使用了默认内建的内存数据库H2。为了方便查看链路,可以选择性启动一个UI。Agent和OAP之间是通过gRPC来发送链路信息的。Agent端维护了一个队列(默认5个channel,每个channel大小为300)和一个线程池(默认1个线程,后面称为发送线程),链路数据采集后主线程(即业务线程)会写入这个队列,如果队列满了,主线程会直接把把数据丢掉(丢的时候会以debug级别打印日志)。发送线程会从队列取数据通过gRPC发送给后端OAP,OAP经过处理后写入存储。为了看得清楚,我把涉及的框架类画到了下面的图里面(格式是:{类名}#{方法名}({方法中调用的重要函数}
):
这里只列举了核心函数,每个函数内部的方法就不赘述了。需要说明的就是Skywalking代码的模块化还是做得很不错,大家跟踪代码的时候可以关注一下功能所属的模块,更有利于学习整个项目或者进行二次开发。
调试
给这些核心方法,打上断点,以Debug模式启动oap-server和simple-springmvc,然后用curl发一个请求,就可以愉快的调试了。
总结
Just read the source code, good luck !
beforeMethod的断点没有拦截住,但是UI的trace界面能看到请求了,应该启动成功了,不知道为什么会这样。
估计有两种可能:
beforeMethod
,检查一下是不是我文中说的那个类;大概率是第1个原因。