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