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發一個請求,就可以愉快的調試了。