分布式系統(tǒng)的測試是一個比較大的話題,在這里,我們僅用幾個阿里云飛天分布式系統(tǒng)測試中比較有特點的實踐方式來闡述一下我們對分布式系統(tǒng)測試的理解,希望對大家有所幫助。
1. 阿里云的分布式系統(tǒng)
飛天是阿里云獨(dú)立開發(fā)的大規(guī)模分布式計算與存儲系統(tǒng),兼有分布式存儲和分布式計算的多重功能。基于飛天大規(guī)模分布式系統(tǒng),我們開發(fā)了彈性計算,海量郵箱服務(wù),Key-Value存儲引擎,結(jié)構(gòu)化數(shù)據(jù)存儲引擎和海量數(shù)據(jù)處理服務(wù)等一系列的上層服務(wù),并且基于這些上層服務(wù),我們運(yùn)營了了Open Table Service和Open Storage Service等多項開放服務(wù)。
2. 測試的層次結(jié)構(gòu)
按照層次和功能,測試可以分為單元測試,功能測試,系統(tǒng)測試,集成測試,端到端測試等若干種測試。這里首先要提的是單元測試,單元測試更像是一種預(yù)防代碼錯誤的手段,而不是檢測代碼錯誤的手段,我們用覆蓋率(包括行覆蓋和分支覆蓋)來檢驗單元測試的質(zhì)量。當(dāng)然我們也都知道這些指標(biāo)不能作為單元測試好壞的標(biāo)準(zhǔn),所以,在項目中,我們也非常注重代碼審查,其中包括了單元測試代碼的審查。
傳統(tǒng)的一般來說,一個產(chǎn)品一般只有對外接口進(jìn)行功能測試和系統(tǒng)測試,但是由于飛天的代碼非常的復(fù)雜,模塊非常多,每個模塊都具有了一個產(chǎn)品的復(fù)雜度。所以,我們在模塊層面也會進(jìn)行功能測試和系統(tǒng)測試,為的是保證系統(tǒng)質(zhì)量能夠得到回歸,在有代碼修改的時候,影響上層業(yè)務(wù)使用的接口或者隱含邏輯一旦發(fā)生變化,能夠第一實踐知曉。功能測試非常注重對接口函數(shù)的覆蓋,并且會對各種參數(shù)的邊界進(jìn)行復(fù)雜的探測,而系統(tǒng)測試比較注重穩(wěn)定性,一致性,性能甚至容災(zāi)能力等各個方面。
在飛天系統(tǒng)內(nèi)部,有專門的集成測試部門,集成測試團(tuán)隊有一個非常大的責(zé)任是通過回歸測試用例來保證系統(tǒng)中的各個模塊,在版本發(fā)布時在一起能夠很好的工作,并對之前每個模塊定下的質(zhì)量驗收標(biāo)準(zhǔn)進(jìn)行驗證。一旦發(fā)現(xiàn)重大風(fēng)險,集成測試部門有權(quán)力不發(fā)布其中某些Feature,如果這個Feature有某種重要的商業(yè)用途,則進(jìn)行推遲整體版本的發(fā)布。這一點可能和傳統(tǒng)軟件不太一樣。當(dāng)分布式軟件已經(jīng)比較成熟的時候,底層很多模塊的Feature在某個版本的刪減,大多數(shù)時候不會影響到用戶的正常使用,但是如果由于這個Feature的發(fā)布導(dǎo)致分布式系統(tǒng)出現(xiàn)不穩(wěn)定或者性能大幅下降,則得不償失。
端到端測試在內(nèi)部有個簡潔的名字叫做E2E Testing。E2E Testing的責(zé)任人需要負(fù)責(zé)向上層使用這個模塊或者系統(tǒng)的用戶方了解具體的需求,這個需求不僅是一個對接口功能的需求,還包含了數(shù)據(jù)量的需求,機(jī)器規(guī)模的需求,吞吐率的需求,延遲時間的需求以及業(yè)務(wù)量在一天或者一周內(nèi)的曲線等等信息,然后設(shè)計E2E的測試程序,能夠盡可能的模擬真實的數(shù)據(jù)情況。在本身上層業(yè)務(wù)就已經(jīng)有數(shù)據(jù)的情況下,還可以通過導(dǎo)入真實業(yè)務(wù)的數(shù)據(jù)進(jìn)行測試。通過長時間的E2E測試,驗證各種指標(biāo)。
一般來說,我們只有在通過了最后的E2E測試之后才能發(fā)布版本,上線讓應(yīng)用使用我們的分布式系統(tǒng)。
3. 注重探索式測試
分布式系統(tǒng)的測試非常的復(fù)雜,你既無法通過窮舉來羅列所有的情況,也不能簡單的對接口和性能進(jìn)行回歸來證明一個版本的合格。因為對于分布式系統(tǒng),不同的數(shù)據(jù)量,不同的壓力,不同的機(jī)器規(guī)模,有可能在代碼里面走的路徑完全不一樣。而且,由于在大的壓力下,磁盤,內(nèi)存和網(wǎng)絡(luò)有可能成為系統(tǒng)的瓶頸。更有甚者,在壓力不均勻的情況下,單臺機(jī)器可能會成為整個系統(tǒng)的瓶頸。所以,我們是沒有辦法設(shè)計這么多的測試用例來分別覆蓋所有的這些分支情況的。
我們有詳細(xì)的監(jiān)控系統(tǒng)可以監(jiān)控整個集群的各種參數(shù),這些參數(shù)不光是硬件參數(shù),更多的是軟件本身通過調(diào)用我們監(jiān)控系統(tǒng)的API來完成對自身某些指標(biāo)的統(tǒng)計。這些統(tǒng)計不光在線上系統(tǒng)能夠起到監(jiān)控的作用,執(zhí)行測試的人員可以通過不斷改變測試的各項參數(shù),結(jié)合這些指標(biāo)的變化進(jìn)行探索式的測試。通常,通過指標(biāo)在某些壓力變化下或者隨著時間推移時的異常行為,測試人員會更容易找到一些深藏的Bug。另外,在系統(tǒng)級測試時,通過對系統(tǒng)內(nèi)部Error Log的監(jiān)控也能達(dá)到很好的效果。
4. 灰盒測試是分布式測試的最有效方法
上面講到了探索式的測試十分重要,但是光有探索,如果測試人員本身不了解系統(tǒng)的一些內(nèi)部邏輯,會出現(xiàn)兩種情況:第一種是,只驗證設(shè)計好的場景,其他一些異常的情況,自己無法解釋,但是本身又不是驗證標(biāo)準(zhǔn),導(dǎo)致很多隱藏的問題最終在線上爆發(fā);第二種是,一個沒頭的蒼蠅,漫無目的的進(jìn)行探索,最終浪費(fèi)了時間,卻達(dá)不到好的效果。在飛天分布式系統(tǒng)測試中,我們要求測試人員必須從方案設(shè)計之初甚至是討論需求的時候就和開發(fā)的同學(xué)在一起討論,測試的同學(xué)需要比開發(fā)更加理解系統(tǒng)的設(shè)計原則。
有一個很典型的例子是,早期,我們內(nèi)部開發(fā)一個基于表結(jié)構(gòu)的存儲引擎時,曾經(jīng)出現(xiàn)過這樣一件事情:測試程序在最終驗證一致性時,一直都是通過的,但是業(yè)務(wù)方和我們一起做E2E測試的時候,會有很低的概率發(fā)現(xiàn)數(shù)據(jù)讀出來是錯誤的。測試人員百思不得其解,最后發(fā)現(xiàn),這份數(shù)據(jù)在寫入的時候,會先在三個地方進(jìn)行修改,但是由于一些時序和鎖的問題,在改過了兩個地方之后就返回成功了,第三個地方是在內(nèi)存中,過一陣子就會被重新刷成正確的值。如果當(dāng)時測試的同學(xué)知道系統(tǒng)里面這些設(shè)計,當(dāng)時就會設(shè)計寫入過程中,對數(shù)據(jù)一致性的檢測,不會在用戶場景中再發(fā)現(xiàn)問題,解決的效率也會因此而提高。
5. 帶壓力和隨機(jī)故障模擬的長時間穩(wěn)定性測試
分布式系統(tǒng)往往是在多臺機(jī)器上運(yùn)行多個進(jìn)程,每個進(jìn)程都是一個獨(dú)立主體,運(yùn)行著自己獨(dú)立的邏輯,各個進(jìn)程都有可能因為軟硬件故障導(dǎo)致邏輯執(zhí)行異常。分布式系統(tǒng)要保證的是任何局部的進(jìn)程異常都不會影響系統(tǒng)的可用性,因此必須要做必要故障模擬和恢復(fù)測試。由于進(jìn)程之間通訊協(xié)議的復(fù)雜,故障類型的多樣,并不能夠模擬所有可能出現(xiàn)故障和進(jìn)程狀態(tài)的組合,也就是說這樣的組合是爆炸的。
在飛天實際的測試過程中,我們采用的是帶背景壓力和隨機(jī)故障模擬的長時間穩(wěn)定性測試。這也是飛天主版本發(fā)布的最后一道關(guān)。背景壓力主要是各個主要模塊的讀寫壓力,還有一些諸如CPU,Memory,Network的資源消耗器,以模擬機(jī)器資源緊張場景。故障模擬操作又比如磁盤錯誤,包括壞盤,只讀等,機(jī)器宕機(jī),重啟,斷網(wǎng),主要模塊進(jìn)程重啟,假死等,這些操作都會按照預(yù)先設(shè)定比例進(jìn)行隨機(jī)組合;在這樣的背景壓力和故障操作下,長時間(至少7x24 小時)持續(xù)運(yùn)行上層應(yīng)用模擬程序/作業(yè)。同時通過在線監(jiān)控工具來檢查系統(tǒng)是否正常。飛天很多重要的bug都是通過這個測試被發(fā)現(xiàn)的。當(dāng)然這類測試也有短處,就是問題調(diào)查需要較長的時間,要求測試人員對系統(tǒng)有較深的了解和診斷能力。
6. 如何保障測試的質(zhì)量
單元測試雖然可以用Coverage工具來檢驗行覆蓋和分支覆蓋,但是功能測試和系統(tǒng)測試在運(yùn)行時,進(jìn)程運(yùn)行在上百上千臺機(jī)器上,很難通過既有的軟件來搜集到coverage數(shù)據(jù)。我們內(nèi)部開發(fā)了一個叫做Log Coverage的工具,能夠通過程序在運(yùn)行過程中輸出的Log信息的多少來判斷測試是否有足夠的覆蓋率。通過拿到生產(chǎn)線上的Log與測試中的Log進(jìn)行比較,會找到我們之前沒有測試到的地方。另外,通過對程序中從來沒有打印出的Error Log的檢查,我們也可以知道有多少異常邏輯我們沒有測試過。