WOT徐冬晨:JVM—Sandbox 基于JVM的非侵入式運(yùn)行期AOP解決方案
原創(chuàng)【51CTO.com原創(chuàng)稿件】2018年5月18-19日,由51CTO主辦的全球軟件與運(yùn)維技術(shù)峰會(huì)在北京召開。來(lái)自全球企業(yè)的技術(shù)精英匯聚北京,暢談軟件技術(shù)前沿,共同探索運(yùn)維技術(shù)的新邊界。而在本次大會(huì)上,除了眾星云集的主論壇環(huán)節(jié),12場(chǎng)分論壇更是各具特色,在19日下午的“微服務(wù)架構(gòu)設(shè)計(jì)”分論壇上,來(lái)自阿里巴巴淘寶技術(shù)質(zhì)量部測(cè)試研發(fā)工程師徐冬晨發(fā)表了精彩演講。身兼主持人和演講人雙重職責(zé)的徐冬晨輕松有活力的開場(chǎng),為我們講述了JVM—Sandbox的產(chǎn)生背景以及它的優(yōu)勢(shì)所在,包括應(yīng)用場(chǎng)景、核心技術(shù)、開源幾個(gè)方面的內(nèi)容。
JVM—Sandbox的產(chǎn)生背景
隨著軟件規(guī)模的擴(kuò)大,系統(tǒng)功能的細(xì)分,要保證阿里巴巴整個(gè)系統(tǒng)穩(wěn)定性,要做許多工具平臺(tái)和監(jiān)控體系,辛勤的開發(fā)測(cè)試人員都需要做哪些工作呢?徐冬晨舉例說(shuō)明,例如我們要做系統(tǒng)限流、流控、故障模擬、信息監(jiān)控、鏈路跟蹤、問(wèn)題定位等等,最應(yīng)該關(guān)心的是,系統(tǒng)架構(gòu)升級(jí)之后,對(duì)基礎(chǔ)業(yè)務(wù)有沒(méi)有影響,測(cè)試者要尋求自動(dòng)化測(cè)試的方法,實(shí)現(xiàn)自動(dòng)化的業(yè)務(wù)回歸。
對(duì)于寫接口測(cè)試的測(cè)試工程師來(lái)說(shuō),他們更想完成的一種方式是線上錄制、線下回放的方式進(jìn)行業(yè)務(wù)回歸,這樣可以大大節(jié)省成本。如果要做這樣的一個(gè)回歸,其方法也是對(duì)方法的入?yún)⒑头祷刂颠M(jìn)行監(jiān)控,或者是監(jiān)控它整條鏈路上面會(huì)不會(huì)出問(wèn)題。下面就是監(jiān)控以及鏈路跟蹤,以及精準(zhǔn)回歸。
徐冬晨列出了四個(gè)比較具體的場(chǎng)景,要保證它的穩(wěn)定性的確需要做很多事情。當(dāng)我們把這些東西做一個(gè)簡(jiǎn)單的抽象之后,上面的這些工具平臺(tái)就做兩件事情:
***是方法的監(jiān)聽與環(huán)繞管控,第二是行鏈路信息的獲取與統(tǒng)計(jì)。以方法的監(jiān)聽與環(huán)繞管控為例,這就是用java,用大家最熟悉的NOP。但是使用NOP也是有些問(wèn)題的,如果我們要有一個(gè)統(tǒng)一的監(jiān)控平臺(tái),監(jiān)控系統(tǒng)代碼與代碼的比例也很重要。她看到過(guò)最夸張的一個(gè)系統(tǒng),監(jiān)控代碼和業(yè)務(wù)代碼的比例是1:2,就是1/3的代碼都是監(jiān)控代碼,而且這種監(jiān)控代碼是比較笨重的,是因?yàn)橐l(fā)散,才能夠把它發(fā)上去,這就是問(wèn)題的所在。
行鏈路方面,為了計(jì)算覆蓋率,如果我們要維持系統(tǒng)的靈活度,就不能因?yàn)橐鲆粋€(gè)問(wèn)題定位,要加一行日志,就重新做系統(tǒng),我們?cè)谧龇€(wěn)定性平臺(tái)配套工具的時(shí)候,需要具備三個(gè)特點(diǎn):
第二:要實(shí)時(shí)生效,因?yàn)閱?wèn)題解決的時(shí)候,要保留現(xiàn)場(chǎng),所以要實(shí)時(shí)生效。
第三:動(dòng)態(tài)可插拔。
要做成這樣子,就需要一個(gè)動(dòng)態(tài)字節(jié)碼增強(qiáng)解決方案。如上面所說(shuō),無(wú)論故障演練、強(qiáng)弱依賴檢測(cè)、流量錄制回訪、問(wèn)題定位還是監(jiān)控體系,如果我們每做這么一個(gè)工具平臺(tái),底層全部去實(shí)現(xiàn)一個(gè)動(dòng)態(tài)字節(jié)碼增強(qiáng)的話,投入的成本是很高的,是有學(xué)習(xí)門檻的。上面衍生出來(lái)的這個(gè)平臺(tái),***都會(huì)作用到一個(gè)系統(tǒng)里面去,其實(shí)他們底層字節(jié)碼增強(qiáng),這段代碼會(huì)不會(huì)相互干擾,都是問(wèn)題。為了解決這些問(wèn)題,為了屏蔽字節(jié)碼增強(qiáng)的技術(shù)高門檻,為了降低研發(fā)和運(yùn)營(yíng)的成本,為了上層多個(gè)模塊可以動(dòng)態(tài)管理。我們就開發(fā)出JVM--Sandbox。
JVM—Sandbox的優(yōu)勢(shì)
JVM—Sandbox既有AOP通用API的便利,又有埋點(diǎn)的靈活,實(shí)時(shí)非侵入的AOP容器。它的功能方面,首先JVM—Sandbox是基于JVMTI技術(shù)規(guī)范,為觀察和改變代碼運(yùn)行結(jié)果提供了即插即用模塊接口的容器。JVM-Sandbox為AOP提供了一個(gè)新的實(shí)現(xiàn)方案——以插樁代替代理。
使用人群:使用字節(jié)碼增強(qiáng)技術(shù),進(jìn)行工具開發(fā)、實(shí)現(xiàn)業(yè)務(wù)功能的開發(fā)、測(cè)試同學(xué)。
核心功能:首先它提供了一個(gè)字節(jié)碼增強(qiáng)統(tǒng)一API。其次它提供了無(wú)切入的容器,它跟你的目標(biāo)機(jī)器之間其實(shí)是隔離的。第三就是我們的容器管理。你可以在JVM—Sandbox基礎(chǔ)上可以掛載多個(gè)模塊,每個(gè)模塊完成它自己的鏈路跟蹤、問(wèn)題定位這些功能是可以同時(shí)掛載的。
利用Sandbox可以實(shí)現(xiàn)哪些功能呢?抽象出來(lái)是入?yún)⒌母兄c改變。返回值的感知與改變以及拋異常。流程的控制,執(zhí)行之前返回,執(zhí)行之前重新構(gòu)造新的一個(gè)結(jié)果對(duì)象進(jìn)行返回,異常之后重新拋出異常或者直接返回一個(gè)正常的結(jié)果,它可以幫你做這些事情。徐冬晨向大家做了一個(gè)簡(jiǎn)單的介紹:
核心操作對(duì)象
首先看核心操作對(duì)象,這是一個(gè)抽象的過(guò)程,我們已經(jīng)在用的有一些開源的工具,包括定位工具、測(cè)試工具,我們抽象出來(lái)其實(shí)就是執(zhí)行之前的觀察和異常觀察,還有執(zhí)行之前的改變以及異常改變。其實(shí)這樣抽象完之后,我們的核心事件是三個(gè),比如說(shuō)我們transform事件,三個(gè)環(huán)節(jié)正常流轉(zhuǎn)和干預(yù)流轉(zhuǎn),以及行事件,行事件其實(shí)就是在每一個(gè)代碼行后面加一個(gè)插裝。
如何與目標(biāo)進(jìn)行隔離和通訊
那么,如何保證Sandbox和目標(biāo)機(jī)器之間是相互隔離的呢?做法非常簡(jiǎn)單,用一句話概括的就是:破壞雙親委派機(jī)制和自定義ClassLoader完成類隔離。向Bootstrap ClassLoader注入一個(gè)Spy類來(lái)完成通訊。這個(gè)是最原始的雙親委派機(jī)制。
破壞雙親委派機(jī)制后,如果要加載一個(gè)類的時(shí)候,它會(huì)先去看當(dāng)前的ClassLoader是否已經(jīng)存在,如果沒(méi)有加載的話,它會(huì)委派它的父親,它的父ClassLoader去問(wèn),你是不是已經(jīng)加載了,如果它也沒(méi)有加載的話,再向上詢問(wèn),一直詢問(wèn)到 Boots trap ClassLoader。這個(gè)是原生的雙親委派機(jī)制。
破壞后的雙親委派機(jī)制變成了什么?要掛載一個(gè)類,它會(huì)先看我當(dāng)前的ClassLoader是不是加載了,如果沒(méi)加載,它會(huì)讓當(dāng)前的ClassLoader嘗試著去加載,也就是它不再向它的父類去詢問(wèn),除非它無(wú)法加載的時(shí)候,它才會(huì)去問(wèn)它的父ClassLoader說(shuō),你是不是已經(jīng)加載了,如果父ClassLoader也沒(méi)有加載的話,它會(huì)讓父ClassLoader嘗試著去加載。這樣就完成了我的目標(biāo)應(yīng)用之間與Sandbox之間的隔離。
其實(shí)Sandbox在啟動(dòng)的時(shí)候會(huì)做一些事件,它會(huì)為每一個(gè)Module,就是上層掛載的Module,以及Sandbox,每個(gè)Module都會(huì)去給它新建一個(gè)ClassLoader,Sandbox自己也會(huì)給它新建一個(gè)ClassLoader。這樣的話我們就完成了Sandbox與Module之間,以及Module與Module之間,以及他們與目標(biāo)應(yīng)用之間的隔離。
通訊其實(shí)就是我們?cè)?Boots trap ClassLoader里面會(huì)注入一個(gè)Spy類,這個(gè)Spy類負(fù)責(zé)目標(biāo)應(yīng)用與Sandbox之間的通訊,不是特別直觀。
如何做到動(dòng)態(tài)插拔
談到如何去實(shí)現(xiàn)動(dòng)態(tài)可插拔,徐冬晨用一句話概括:transform方法形變?cè)止?jié)碼,事件監(jiān)聽表管理模塊。為什么要有這塊,其實(shí)不管是對(duì)于一個(gè)系統(tǒng)來(lái)講,我們將系統(tǒng)上面attach一個(gè)東西,我們最關(guān)心的是,我能不能還原,有能力再恢復(fù)回去。你增加的一些東西,你增加Sandbox和這些模塊之后,對(duì)我的系統(tǒng)到底它是怎么去作用上去的,它在哪里發(fā)生了形變,它的怎么作用上去的,我系統(tǒng)還能不能還原。
這樣的話,其實(shí)這幅圖就是表現(xiàn)的是這樣的一件事情,我們先看一下我們的形變發(fā)生在哪里,對(duì)JVM已經(jīng)加載的類進(jìn)行過(guò)濾(過(guò)濾器由Module告知sandbox),找到需要形變的類。拿到我要形變的類之后,他會(huì)通過(guò)一個(gè)形變通道,通過(guò)這個(gè)形變通道,形變通道上面都有哪些事情,都有哪些形變,就是由我們Sandbox加載的各個(gè)Module來(lái)決定的。
這邊相當(dāng)于是一個(gè)事件監(jiān)聽表,這個(gè)Module對(duì)這個(gè)類發(fā)生了一次形變。如果我新增加一個(gè)Module會(huì)怎么樣,所有的類會(huì)重新過(guò)濾一次,對(duì)Module指定重新加載形變。如果我減少一個(gè)Module。同樣的,需要先過(guò)濾出Module指定的類,然后進(jìn)行形變。這樣的話,從這個(gè)上面我們可以看到,如果我把Sandbox上面所有的模塊全部卸載掉之后,整個(gè)通道就是沒(méi)有形變的,沒(méi)有形變的話,就是一個(gè)class而變成這個(gè)數(shù),然后再變成一個(gè)class,其實(shí)它就是沒(méi)有形變,整個(gè)代碼其實(shí)也就還原了。
在使用Sandbox過(guò)程中,如果你只掛載Sandbox,本身對(duì)你原碼是沒(méi)有影響的,如果你在Sandbox基礎(chǔ)上掛載了Module,Module決定了你影響了哪些類和哪些方法。當(dāng)你把一個(gè)Module卸載掉之后,整個(gè)形變也就消失了,這是動(dòng)態(tài)可插拔來(lái)完成的。
如上圖,這是JVM-Sandbox的一個(gè)整體的架構(gòu),這個(gè)里面比較底層就是在JVMTI架構(gòu)體系上面去構(gòu)建的,做了一些代碼編織的框架。我們可以對(duì)它進(jìn)行方法調(diào)用的環(huán)繞編織,方法流程的干預(yù),方法路徑的編織,這樣的一些過(guò)程。沙箱會(huì)進(jìn)行事件分發(fā),事件監(jiān)聽,事件注銷和事件的一些處理。這樣其實(shí)就完成了,我們完成了模塊的管理,上層我們會(huì)做一些模塊管理的事情。
我們看這個(gè)里面,多出來(lái)的一塊其實(shí)這一部分,這部分就是在Sandbox里面,它有個(gè)HTTP服務(wù)器,它的作用是整個(gè)Sandbox掛起之后,你的模塊是需要掛載、卸載、激活、啟動(dòng)這些操作時(shí),服務(wù)器來(lái)控制它。當(dāng)時(shí)比較方便的一種方式就是HTTP企圖去控制,所以它里面增加了HTTP的服務(wù)器。所以你在Sandbox掛載之后,上層的模塊,其實(shí)都可以通過(guò)HTTP請(qǐng)求然后加以控制,去控制它的啟動(dòng)、卸載和加載這樣的一些事情。
Sandbox本身是已經(jīng)開源的,能夠拿到它所有的原代碼。我們希望是有更多的同學(xué),能夠想到更多的應(yīng)用場(chǎng)景,并且開源出來(lái)供大家使用。
本次WOT峰會(huì)講師演講稿件由51CTO采編整理,如欲了解更多,敬請(qǐng)登錄www.sunluscious.com.cn進(jìn)行查看。


























