Skywalking主要由Agent、OAP、Storage、UI四大模塊組成(如下圖):
Agent和業(yè)務(wù)程序運行在一起,采集鏈路及其它數(shù)據(jù),通過gRPC發(fā)送給OAP(部分Agent采用http+json的方式);OAP還原鏈路(圖中的Tracing),并分析產(chǎn)生一些指標(圖中的Metric),最終存儲到Storage中。本文從源碼角度來串聯(lián)一下這整個流程( 基于目前最新的Skywalking 8.0.1 )。
源碼編譯Skywalking
本地調(diào)試必須先從源碼編譯Skywalking,有兩種方式,一種是從GitHub拉取代碼,一種是從Apache Skywalking的release頁面下載代碼。區(qū)別在于GitHub上面的代碼是使用git module管理的,拉取下來需要執(zhí)行一系列操作,最主要的是沒有科學上網(wǎng)的話,速度比較慢。Release頁面下載的是已經(jīng)把依賴關(guān)系全部整理好的代碼,整個源碼包不到3MB,還有很多國內(nèi)鏡像地址,所以下載非常快。兩種我都使用過,我的建議是:如果你想看歷史提交記錄或者想持續(xù)跟上游版本的話,就選用從GitHub拉取代碼的方式;如果你想方便或者從GitHub clone超級慢的話,建議直接從Release處下載。不管哪種,編譯以及導入IDEA或Eclipse官方文檔寫的都比較詳細,我就不做翻譯了,基本都是命令操作,英文不好也看得懂(just copy-and-paste~~): How to build .
源碼編譯成功以后(務(wù)必保證編譯成功),就可以準備進行調(diào)試了。
源碼流程簡析及調(diào)試
這里通過一個簡單的Spring MVC程序來演示如何調(diào)試Agent和OAP。
創(chuàng)建一個Spring MVC程序
在Skywalking項目下增加一個簡單的Spring MVC模塊(注意這里一定要以Skywalking項目module的方式添加),這里我創(chuàng)建了一個名叫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路徑換成你自己的路徑。源碼如果編譯成功的話,源碼根目錄下面會出現(xiàn)這個skywalking-agent目錄,并且里面會有這個skywalking-agent.jar。如下圖:
這樣調(diào)試用的示例程序以及Skywalking的agent注入也配置好了。先別啟動,接下來還需要啟動OAP。
啟動OAP
如果想先只調(diào)試agent的話,可以單獨下載一個Skywalking的二進制(編譯完以后,根目錄下的dist目錄也有二進制安裝包),本地啟動(參考我之前的文章)即可。第一次調(diào)試的話,我建議agent和OAP單獨調(diào)試,因為兩者有一些公用代碼,在一個工程里面啟動的話,容易造成混淆。分開調(diào)試的話就本地單獨起一個Skywalking就行,這里講直接在項目里面啟動一個OAP的方式。
啟動OAP非常簡單,OAP的代碼是源碼根目錄下的oap-server,入口函數(shù)是 org.apache.skywalking.oap.server.starter 包下面的OAPServerStartUp類。直接啟動即可。
需要注意的是這樣只啟動了OAP,為了方便查看還原的鏈路(不啟動也不影響調(diào)試,不看Web的直接跳過),我們再手動啟動一個Web UI。直接在Skywalking安裝目錄下面(注意是二進制安裝目錄,不是源碼目錄)的webApp目錄下執(zhí)行: java -jar skywalking-webapp.jar 即可。默認訪問地址為 http://127.0.0.1 :8080/。
OAP和UI(optional)啟動好以后,就可以開始調(diào)試了。
流程簡析
啟動調(diào)試之前,我先簡單介紹一下數(shù)據(jù)流向以及一些關(guān)鍵的函數(shù),方便提前打斷點。整個數(shù)據(jù)流如下圖:
這里我們先創(chuàng)建了一個Spring MVC程序simple-springmvc,并且配置了javaagent,這樣Skywalking agent就會以字節(jié)碼注入的方式運行在simple-springmvc里面。當我們使用curl命令發(fā)送請求時,就會產(chǎn)生鏈路數(shù)據(jù)。需要注意的是,Skywalking默認已經(jīng)實現(xiàn)了Spring MVC的插件 {源碼根目錄}/skywalking-agent/plugins/apm-springmvc-annotation-commons-8.0.1.jar ,對應的源碼是 {源碼根目錄}/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons 。它的增強函數(shù)就是在這個模塊下的 AbstractMethodInterceptor 類中實現(xiàn)的,給這個類的 beforeMethod 方法打個斷點(為了節(jié)省篇幅,省略了一些不重要的代碼),就可以觀察數(shù)據(jù)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,后端存儲使用了默認內(nèi)建的內(nèi)存數(shù)據(jù)庫H2。為了方便查看鏈路,可以選擇性啟動一個UI。Agent和OAP之間是通過gRPC來發(fā)送鏈路信息的。Agent端維護了一個隊列(默認5個channel,每個channel大小為300)和一個線程池(默認1個線程,后面稱為發(fā)送線程),鏈路數(shù)據(jù)采集后主線程(即業(yè)務(wù)線程)會寫入這個隊列,如果隊列滿了,主線程會直接把把數(shù)據(jù)丟掉(丟的時候會以debug級別打印日志)。發(fā)送線程會從隊列取數(shù)據(jù)通過gRPC發(fā)送給后端OAP,OAP經(jīng)過處理后寫入存儲。為了看得清楚,我把涉及的框架類畫到了下面的圖里面(格式是: {類名}#{方法名}({方法中調(diào)用的重要函數(shù)} ):
這里只列舉了核心函數(shù),每個函數(shù)內(nèi)部的方法就不贅述了。需要說明的就是Skywalking代碼的模塊化還是做得很不錯,大家跟蹤代碼的時候可以關(guān)注一下功能所屬的模塊,更有利于學習整個項目或者進行二次開發(fā)。
給這些核心方法,打上斷點,以Debug模式啟動oap-server和simple-springmvc,然后用curl發(fā)一個請求,就可以愉快的調(diào)試了。






