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