晉文明,李昌建,錢 巨,2
(1.南京航空航天大學(xué) 計算機科學(xué)與技術(shù)學(xué)院,江蘇 南京 210016;2.江蘇省軟件新技術(shù)與產(chǎn)業(yè)協(xié)同創(chuàng)新中心,江蘇 南京 210023)
隨著互聯(lián)網(wǎng)的普及,以電子商務(wù)網(wǎng)站(eBay、亞馬遜、淘寶)和典型云服務(wù)(如Gmail、OneDrive)為代表的許多大型軟件系統(tǒng)承受越來越多的訪問流量[1],這給軟件系統(tǒng)的服務(wù)質(zhì)量帶來了一定的不確定性。負載測試作為軟件測試的一種,是為了檢測系統(tǒng)在負載方面的相關(guān)問題從而對系統(tǒng)進行評估的過程[2]。因而,為了保障大型軟件系統(tǒng)服務(wù)質(zhì)量的可靠性,有必要對其進行有效的大規(guī)模負載測試[3]。人們研發(fā)了LoadRunner[4]、JMeter[5]、httperf[6]等一系列負載測試工具[7],但這些測試工具一般存在兩個問題。一是支持的腳本類型過于單一化,二是僅支持采用進程或線程并發(fā)方式發(fā)起負載,負載生成機制不夠豐富,負載生成消耗不夠優(yōu)化,在資源受限的情況下難以生成較大規(guī)模的負載。同時,這些測試工具在分布式環(huán)境下的部署流程較為復(fù)雜,加大了測試的難度。云測試能夠降低負載測試的實施難度,在云負載測試服務(wù)方面,以阿里云PTS[8]為代表的云測試系統(tǒng)同樣存在負載生成不夠優(yōu)化的問題,執(zhí)行大規(guī)模負載測試需要大量云資源作為支撐,增加了測試成本。在云測試工具的相關(guān)研究中,文獻[9]實現(xiàn)了一個IaaS云平臺測試系統(tǒng),該測試系統(tǒng)的負載生成依賴于Apache JMeter來模擬并發(fā)負載,只支持以線程并發(fā)方式執(zhí)行JMeter測試腳本,難以在有限資源下生成較大規(guī)模的負載。文獻[10]實現(xiàn)了一種自動化云測試平臺,基于測試工具Selenium執(zhí)行測試腳本以實現(xiàn)自動化測試。但Selenium引擎執(zhí)行開銷非常大,導(dǎo)致能夠發(fā)起的負載規(guī)模十分有限,并且該平臺主要用于功能測試。文獻[11]設(shè)計了一個基于云計算的軟件測試系統(tǒng)框架,使用動態(tài)優(yōu)先權(quán)調(diào)度算法實現(xiàn)測試任務(wù)的資源分配與執(zhí)行。其資源分配沒有考慮測試任務(wù)的資源特征,僅以測試執(zhí)行時間為優(yōu)化目標(biāo),可能導(dǎo)致測試資源分配不夠優(yōu)化。文獻[12]也設(shè)計了一個云性能測試工具,支持分布式測試服務(wù)器間的同步控制功能,其工作流程為:由控制服務(wù)器向測試服務(wù)器發(fā)送測試執(zhí)行命令,測試命令保證了所有接收到命令的測試服務(wù)器同步執(zhí)行測試。但是該測試工具只能保證負載測試在初始狀態(tài)的同步性,無法保證整個測試在不同階段的同步性。因而,現(xiàn)有研究的測試工具往往存在負載生成機制不夠豐富、測試資源分配不夠優(yōu)化等問題,導(dǎo)致大規(guī)模負載測試成本過高且不易實施。
為了能夠高效地實施大規(guī)模負載測試,該文研究了多類型的負載生成、智能化測試資源分配和分布式負載同步控制技術(shù),設(shè)計和實現(xiàn)了一種基于OpenStack[13]的大規(guī)模云負載測試平臺。該平臺支持進程、線程和協(xié)程負載并發(fā)機制,結(jié)合多類型測試腳本以生成客戶端負載;能夠預(yù)測負載測試任務(wù)的資源需求,并為其確定云測試主機(OpenStack中部署了測試執(zhí)行引擎的虛擬節(jié)點)資源,實現(xiàn)智能化測試資源分配;采用同步控制算法保證不同測試主機間測試進度的同步性。該平臺為大規(guī)模負載測試提供了一個功能豐富、經(jīng)濟易用的平臺,輔助以智能化測試資源分配、分布式負載同步控制等功能保證了負載測試的執(zhí)行效果,同時可幫助降低大規(guī)模負載測試的實施難度。
基于OpenStack的大規(guī)模云負載測試平臺的總體界面如圖1所示。平臺通過導(dǎo)入測試腳本,對待測Web應(yīng)用進行負載測試,收集相關(guān)指標(biāo)數(shù)據(jù),并據(jù)此來分析承載Web應(yīng)用的被測云服務(wù)設(shè)施的相關(guān)表現(xiàn)情況。該平臺支持云負載測試執(zhí)行、分布式負載同步控制、智能化測試資源分配等主要功能,此外,還支持測試腳本管理、測試結(jié)果管理等輔助功能。
圖1 平臺總體界面
為保證靈活性,云測試平臺采用如圖2所示的物理結(jié)構(gòu),整個系統(tǒng)由前端Dashboard、測試控制中心CloudTest、測試執(zhí)行引擎TestAgent、監(jiān)控器VMMonitor四大基本模塊構(gòu)成。
圖2 系統(tǒng)物理結(jié)構(gòu)
前端Dashboard是一個Web前端頁面,在該頁面下,包含測試集群、云應(yīng)用、云主機、測試腳本、云應(yīng)用性能測試、測試結(jié)果等主頁面。測試管理中心CloudTest一方面通過暴露的RESTful接口與Dashboard交互;另一方面與測試執(zhí)行引擎交互,接收實時負載執(zhí)行數(shù)據(jù);與監(jiān)控器交互,接收主機的實時監(jiān)控數(shù)據(jù)。測試執(zhí)行引擎是執(zhí)行負載測試的主要模塊,對用戶配置的負載測試任務(wù)進行解析,按照預(yù)設(shè)的負載變化策略在指定的時間節(jié)點上生成相應(yīng)規(guī)模的負載,發(fā)起對被測目標(biāo)應(yīng)用的網(wǎng)絡(luò)調(diào)用,并統(tǒng)計每個負載的執(zhí)行數(shù)據(jù),匯報至測試控制中心。監(jiān)控器周期性地采集宿主機的資源使用信息匯報至測試控制中心。其中,測試控制中心、測試執(zhí)行引擎、監(jiān)控器等模塊均支持部署在私有云OpenStack上,亦可部署在公有云環(huán)境中。
由于進程或線程并發(fā)是多個子例程通過操作系統(tǒng)時間片輪轉(zhuǎn)來實現(xiàn),占據(jù)資源大、任務(wù)切換代價較高,導(dǎo)致進程或線程并發(fā)方式下單機的負載執(zhí)行性能一般。協(xié)程是一種程序組件[14],其高性能主要體現(xiàn)在如下幾個方面:(1)協(xié)程間的切換由用戶空間控制,無需操作系統(tǒng)參與,上下文切換的開銷極?。?2)協(xié)程本身是輕量級的,資源占用小,基本不存在數(shù)量限制;(3)協(xié)程具有非阻塞異步I/O模型的特性,在IO密集型程序中執(zhí)行性能非常高。因而,協(xié)程可模擬大規(guī)模負載。
該云測試平臺在支持進程、線程負載并發(fā)機制的基礎(chǔ)上,引入了協(xié)程負載并發(fā)機制。Quasar為Java提供了高性能的輕量級線程,使用Quasar Fiber可實現(xiàn)Java協(xié)程,平臺利用Quasar Fiber和異步Appache Http組件實現(xiàn)并發(fā)客戶端負載的生成,其主要流程如圖3所示。
圖3 協(xié)程執(zhí)行流程
如圖4所示,平臺利用云環(huán)境(如,OpenStack)中的測試主機來發(fā)起大規(guī)模負載測試,通過分布式并發(fā)并行調(diào)用產(chǎn)生對目標(biāo)應(yīng)用的并發(fā)客戶端負載。測試主機上具體由訪問被測目標(biāo)應(yīng)用的任務(wù)進程、任務(wù)進程內(nèi)的任務(wù)線程或任務(wù)協(xié)程來完成網(wǎng)絡(luò)訪問。不同任務(wù)進程、任務(wù)線程以及任務(wù)協(xié)程模擬了不同的虛擬客戶端,總的任務(wù)進程、任務(wù)線程和任務(wù)協(xié)程的數(shù)量大致對應(yīng)了所生成客戶端負載的總體規(guī)模。
圖4 負載并發(fā)機制
傳統(tǒng)負載測試工具支持的腳本過于單一化,導(dǎo)致無法很好地適用于不同的測試目標(biāo)和目的。文中的云測試平臺支持執(zhí)行Selenium、JMeter、Java等類型的測試腳本,基于多類型測試腳本來生成客戶端負載。腳本間比較如表1所示。
表1 不同類型的測試腳本
如Selenium腳本示例所示的腳本能夠描述Web頁面的打開、點擊等行為,可表達復(fù)雜動態(tài)頁面Web應(yīng)用上的動作,靈活性最強,能測試的Web應(yīng)用類型最廣。但是腳本的執(zhí)行開銷非常大,并且依賴Python引擎支撐,而當(dāng)前大部分Python引擎的并發(fā)執(zhí)行性能都較弱。因而,在資源較為一般的測試主機上,很難發(fā)起較大規(guī)模的負載。
from selenium import webdriver
st=tester();
class Addbook(unittest.TestCase):
def setUp(self):
self.driver=webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url="http://192.168.1.130/"
def test_addbook(self):
driver=self.driver
driver.get(self.base_url+"/BookManager?method=list")
driver.find_element_by_link_text(u"添加圖書").click()
if __name__ == "__main__":
unittest.main()
如JMeter腳本示例所示的腳本是一段XML配置,描述了一組針對Web應(yīng)用的網(wǎng)絡(luò)訪問報文。利用JMeter腳本執(zhí)行引擎可以提取JMeter腳本中所描述的網(wǎng)絡(luò)報文,對其進行重放。JMeter腳本執(zhí)行引擎的一個優(yōu)勢是可以直接利用其他工具的腳本。相對Selenium腳本,JMeter腳本的負載生成能力明顯更強。然而,JMeter腳本不能像Python腳本那樣隨意修改,輕松插入新的動作代碼,因此,靈活性弱于Selenium腳本。
class="HTTPSamplerProxy" testname="Open blazemeter. com"> entType="Arguments"> weibo.com 如Java腳本示例所示的腳本使用Apache HTTP庫通信,對網(wǎng)絡(luò)的訪問較為直接,避免JMeter中的腳本解釋開銷。Java腳本支持協(xié)程等高性能機制,依賴于協(xié)程并發(fā)方式執(zhí)行腳本,相對于其他測試腳本,其資源開銷最低,執(zhí)行效率最高,在同樣的CPU和內(nèi)存配置下,腳本能夠更大規(guī)模的并發(fā)。然而,此種腳本需要手工編寫,且需要事先編譯,制作代價較高。 import java.util.*; public class TestCase implements Runnable{ private static String URL = "http://www.baidu.com"; public void run() { ResultStatus status = ResultStatus.PASSED; try { Response response = Request.Get(URL).execute(); StatusLinestatusLine = response. returnResponse().getStatusLine(); int statusCode = statusLine.getStatusCode(); if(statusCode!=200) { status = ResultStatus.FAILED; } } } } 在實施大規(guī)模負載測試的過程中,由于單臺機器的硬件資源能力較為有限,難以生成較大規(guī)模的負載。為了生成足夠規(guī)模的負載,往往需要多臺測試主機同時提供測試服務(wù)。分配的主機資源如果過少,將會導(dǎo)致無法發(fā)起相應(yīng)規(guī)模的負載;分配的主機資源如果過多,將會造成測試資源的冗余。因而,如何為負載測試任務(wù)分配資源顯得十分關(guān)鍵。 文中的云測試平臺支持智能化測試資源分配功能。平臺以每一處理周期內(nèi)用戶下達的負載測試任務(wù)的序列T= 對于目標(biāo)負載測試任務(wù)序列T中的每個測試任務(wù)Ti,云測試平臺按照如下流程預(yù)測得到其資源需求向量R= 首先,從平臺的測試腳本池中選擇目標(biāo)負載測試任務(wù)(假設(shè)目標(biāo)負載規(guī)模為5 000)的測試腳本(如,edit_order.jar腳本)作為訓(xùn)練的對象,平臺支持為其自動配置一個較小負載規(guī)模(一般為500)的測試活動,并在單臺測試主機(一般配置為2 CPU核心和4 GB內(nèi)存,其資源向量可用CPU核心數(shù)和內(nèi)存大小的二維向量(2, 4)表示)上自動執(zhí)行該測試活動,完成預(yù)熱執(zhí)行工作。 第二步,在預(yù)熱執(zhí)行過程中,部署在測試主機上的測試執(zhí)行引擎收集實時負載執(zhí)行數(shù)據(jù)L(t)={(t,load)},監(jiān)控器實時收集測試主機的資源使用數(shù)據(jù)R(t)={(t,R)},使用處理函數(shù)M(X,Y)將負載執(zhí)行數(shù)據(jù)和資源使用數(shù)據(jù)融合,使其在時間節(jié)點上對應(yīng),得到負載_資源數(shù)據(jù)LR,可表示為: LR=M(L(t),R(t))={(load,R)} 第三步,以負載資源數(shù)據(jù)LR作為訓(xùn)練模型的輸入,通過回歸學(xué)習(xí)方法,抽取edit_order.jar腳本的資源模型(可用映射f:load→R表示,其輸入為目標(biāo)負載規(guī)模load,輸出為負載測試任務(wù)所需資源向量R)。如圖5所示,其中CPU資源模型為fcpu(x)=0.284lnx+0.07,內(nèi)存資源模型為fram(x)=0.013x2/3+0.29,從而預(yù)測負載測試任務(wù)的資源需求為(2.49,4.09),則至少需要3臺配置為1核心CPU和2 GB內(nèi)存的云測試主機提供測試服務(wù)。重復(fù)上述步驟,預(yù)測出目標(biāo)負載測試任務(wù)序列中每個測試任務(wù)的資源需求向量。 圖5 資源模型 對于待分配資源的負載測試任務(wù)序列T,基于4.1節(jié)所述的方法可預(yù)測其資源需求向量為R。云測試平臺采用一種基于遺傳演化的多目標(biāo)資源分配算法為T分配云測試資源,其總體執(zhí)行流程如圖6所示。在資源分配的過程中,為了減少資源的浪費,降低測試的成本開銷,并保證測試的執(zhí)行效率,算法以最小資源冗余、最低測試執(zhí)行成本為一組優(yōu)化目標(biāo),關(guān)于目標(biāo)函數(shù)的定義這里不再贅述。 圖6 算法執(zhí)行流程 首先,平臺支持獲取當(dāng)前云測試環(huán)境OpenStack的網(wǎng)絡(luò)拓撲結(jié)構(gòu)圖(物理節(jié)點、虛擬節(jié)點以及路由節(jié)點間的網(wǎng)絡(luò)通信結(jié)構(gòu))。依據(jù)網(wǎng)絡(luò)拓撲結(jié)構(gòu)以及虛擬節(jié)點可用資源等信息生成負載測試任務(wù)序列T的一組初始化資源分配方案,并按照向量S所示編碼方式對資源分配方案進行編碼。 然后,對編碼后的資源分配方案進行交叉、變異操作,形成初步子代種群。對初步子代種群中的資源分配方案進行修復(fù)并優(yōu)化,確保分配方案的可行性與優(yōu)越性;父子種群合并,將合并后的種群中的優(yōu)良分配方案選擇進入新的種群,使得分配方案的最小資源冗余、最低測試執(zhí)行成本等目標(biāo)朝著更優(yōu)的方向進化,從而完成一次進化。重復(fù)上述過程直至預(yù)設(shè)進化代數(shù),最終得到目標(biāo)負載測試任務(wù)序列的最優(yōu)資源分配方案。 最后,平臺依據(jù)最優(yōu)資源分配方案,將負載測試任務(wù)序列中的各測試任務(wù)分配到對應(yīng)的OpenStack云測試主機上,并由平臺對云測試主機進行統(tǒng)一化管理。分配完成后,即可開始目標(biāo)負載測試任務(wù)的執(zhí)行,平臺收集實時負載執(zhí)行數(shù)據(jù)以對測試進行運行時監(jiān)控,從而形成對各負載測試執(zhí)行的性能評估。 給定如圖7所示的云測試環(huán)境G和一組資源需求已知的負載測試任務(wù)T=,在文中的云測試平臺上分別采用多目標(biāo)資源分配和隨機資源分配方法為其分配測試資源。實驗結(jié)果如圖7所示,在相同規(guī)模的云測試環(huán)境下,對于相同的負載測試任務(wù)序列,多目標(biāo)資源分配方法相較于傳統(tǒng)的隨機分配方法,能夠減少云測試主機的占用數(shù),降低測試執(zhí)行成本。 圖7 實驗環(huán)境與實驗結(jié)果 隨著測試的執(zhí)行,不同測試主機上的測試進度可能差別越來越大,最終導(dǎo)致不同機器上對待測Web應(yīng)用的訪問可能不是按預(yù)期來并發(fā)的,所產(chǎn)生的負載壓力與預(yù)期設(shè)定不符。 文中的云測試平臺支持分布式負載同步控制功能。對每個負載測試配置,測試平臺支持為其設(shè)定一組同步集合點,典型的同步策略包括按比例同步,即當(dāng)一定比例的任務(wù)進入同步點后,已經(jīng)進入同步點的任務(wù)即可繼續(xù)向下執(zhí)行;以及按絕對數(shù)量同步,即當(dāng)某指定數(shù)量的任務(wù)進入同步點后,已經(jīng)進入同步點的任務(wù)即可繼續(xù)向下執(zhí)行。 在為負載測試配置設(shè)定了同步集合點后,可以在測試腳本中添加同步集合點進入原語,如含同步集合點的測試腳本示例中的st.rendezvous("dosearch")。測試腳本執(zhí)行到該同步集合點語句時,會進入等待狀態(tài),直到退出同步集合點的條件得到滿足。該同步集合點可以使得該集合點后的語句都盡可能在同一時刻得到執(zhí)行。 st=tester(); class Baidu(unittest.TestCase): def setUp(self): self.driver=webdriver.Firefox() self.base_url="https://www.baidu.com/" def test_baidu(self): driver=self.driver driver.get(self.base_url + "/") st.rendezvous("dosearch") driver.find_element_by_id("su").click() if __name__ == "__main__": unittest.main() 同步集合點僅對負載變化策略中同一時刻點上發(fā)起的負載有效。對于原定測試計劃中理論上應(yīng)發(fā)生于同一時刻的客戶端負載,平臺采用同步控制算法以確保同步集合點能夠有效發(fā)揮作用,保證不同測試主機間測試進度的同步性。同步控制算法的總體流程如下: input:待執(zhí)行的負載測試活動 output:每個步驟上的有效負載規(guī)模的集合?(load) begin 獲取負載變化策略,測試集群的主機數(shù)量testClusterSize和負載變化步驟數(shù)steps; while step 讀取當(dāng)前步驟上預(yù)設(shè)的負載規(guī)模loadScale; whiletrue 為每個負載啟動一個負載執(zhí)行器; 在負載執(zhí)行器內(nèi)執(zhí)行測試腳本; 腳本進入renderzvous語句后,向測試主機發(fā)出enterRendezvous消息; 當(dāng)前等待執(zhí)行的腳本數(shù)actualLoad++; if actualLoad==loadScale(測試主機滿足退出同步集合點的條件) then 測試主機向測試控制中心發(fā)出enterRendezvous的消息; 等待執(zhí)行的測試主機數(shù)agentCount++; actualLoad→?(load); while agentCount 阻塞等待執(zhí)行的測試主機上進入同步集合點的腳本; end while 測試控制中心向測試集群中各測試主機發(fā)出exitRendezvous; 測試主機將同步退出消息轉(zhuǎn)發(fā)給各個正在等待的腳本; 腳本收到消息后,繼續(xù)向下執(zhí)行測試腳本中的后續(xù)語句; end end while end while return ?(load); end 研究了多類型負載生成、智能化測試資源分配和分布式負載同步控制技術(shù),實現(xiàn)了一種基于OpenStack的大規(guī)模云負載測試平臺。該平臺具有如下特性:支持進程、線程和協(xié)程負載并發(fā)機制,結(jié)合多類型測試腳本生成客戶端負載,相較于已有測試工具,客戶端負載生成更為高效;支持智能化測試資源分配功能,實現(xiàn)面向云負載測試任務(wù)的資源優(yōu)化分配;支持分布式負載同步控制功能,保證不同測試主機上網(wǎng)絡(luò)活動的并行性。該測試平臺為測試人員實施大規(guī)模負載測試提供了一個功能豐富、經(jīng)濟易用的平臺,能夠降低大規(guī)模負載測試的難度。4 智能化測試資源分配
4.1 測試資源預(yù)測
4.2 測試資源分配
5 分布式負載同步控制
6 結(jié)束語