簡述性能測試
提起性能測試,可能移動APP的從業人員會感覺比較混淆,因為在客戶端(Android、iOS)中也有性能測試專項,主要涉及的是APP的啟動時間、內存、包大小、幀率,流量等客戶端相關的指標。在本博客之前的文章中,也包含了一些客戶端性能測試的內容。需要說明的是,本文所講解的性能測試都是針對服務器端,尤指Web系統的,與移動APP的性能測試完全是不同的領域。
那么,什么是服務端的性能測試呢?
先從大家都熟悉的功能測試說起吧。例如,我們要測試一個搜索功能,那么我們測試時,就會輸入搜索關鍵詞,點擊搜索按鈕,然后再去查看搜索結果,看結果是否跟我們輸入的搜索關鍵詞匹配,如果匹配則說明搜索功能實現正確。
那如何對該功能進行性能測試呢?
答案就是,N個人同時進行功能性操作的同時,在確保功能實現正確的前提下,考察服務端應用程序的各項性能指標,以及服務器硬件資源的使用情況。
當然,這個答案比較簡單粗暴,但是它仍然包含了性能測試的基本特點:
- 以功能實現正確為前提
- 通常有一定的並發用戶
- 重點考察服務器端在一定並發壓力下的性能指標
最后,再明確下性能測試的目的。通常,對服務器端應用程序開展性能測試,是為了驗證軟件系統是否能夠達到預期的性能指標,同時發現軟件系統中存在的性能瓶頸,從而實現優化系統的目的。
性能測試方法的核心
根據不同的測試目的,性能測試可以分為多種類型,常見的有如下幾類:
- 基准測試(Standard Testing)
- 負載測試(Load Testing)
- 壓力測試(Stress Testing)
- 疲勞強度測試
首先說下基准測試。基准測試指的是模擬單個用戶執行業務場景時,考察系統的性能指標。嚴格意義上來講,基准測試並不能算作性能測試范疇,它跟功能測試並沒有太大區別。差異在於,基准測試的目的更多地是關注業務功能的正確性,或者說驗證測試腳本的正確性,然后,將基准測試時采集得到的系統性能指標,作為基准測試結果,為后續並發壓力測試的性能分析提供參考依據。
負載測試,主要指的是模擬系統在正常負載壓力場景下,考察系統的性能指標。這里說的正常負載,主要是指用戶對系統能承受的最大業務負載量的期望值,即預計系統最大應該支持多大用戶的並發量。通過負載測試,目的是驗證系統是否能滿足預期的業務壓力場景。
和負載測試的概念比較接近的是壓力測試。通俗地講,壓力測試是為了發現在多大並發壓力下系統的性能會變得不可接受,或者出現性能拐點(崩潰)的情況。在加壓策略上,壓力測試會對被測系統逐步加壓,在加壓的過程中考察系統性能指標的走勢情況,最終找出系統在出現性能拐點時的並發用戶數,也就是系統支持的最大並發用戶數。
最后再說下疲勞強度測試。其實疲勞強度測試的加壓策略跟負載測試也很接近,都是對系統模擬出系統能承受的最大業務負載量,差異在於,疲勞強度測試更關注系統在長時間運行情況下系統性能指標的變化情況,例如,系統在運行一段時間后,是否會出現事務處理失敗、響應時間增長、業務吞吐量降低、CPU/內存資源增長等問題。
通過對比可以發現,不同的性能測試類型,其本質的差異還是在加壓策略上,而采用何種加壓策略,就取決於我們實際的測試目的,即期望通過性能測試發現什么問題。明白了這一點,性能測試類型的差異也就不再容易混淆了。
結論要點1:性能測試手段的重點在於加壓的方式和策略。
性能瓶頸定位的核心
在前面頻繁地提到了性能指標,那性能指標究竟有哪些,我們在性能測試的過程中需要重點關注哪些指標項呢?
從維度上划分,性能指標主要分為兩大類,分別是業務性能指標和系統資源性能指標。
業務性能指標可以直觀地反映被測系統的實際性能狀況,常用的指標項有:
- 並發用戶數
- 事務吞吐率(TPS/RPS)
- 事務平均響應時間
- 事務成功率
而系統資源性能指標,主要是反映整個系統環境的硬件資源使用情況,常用的指標包括:
- 服務器:CPU利用率、處理器隊列長度、內存利用率、內存交換頁面數、磁盤IO狀態、網卡帶寬使用情況等;
- 數據庫:數據庫連接數、數據庫讀寫響應時長、數據庫讀寫吞吐量等;
- 網絡:網絡吞吐量、網絡帶寬、網絡緩沖池大小;
- 緩存(Redis):靜態資源緩存命中率、動態數據緩存命中率、緩存吞吐量等;
- 測試設備(壓力發生器):CPU利用率、處理器隊列長度、內存利用率、內存交換頁面數、磁盤IO狀態、網卡帶寬使用情況等。
對於以上指標的具體含義我就不在此進行逐一說明了,大家可以自行搜索,務必需要搞清楚每個指標的概念及其意義。可能有些指標在不同的操作系統中的名稱有些差異,但是基本都會有對應的指標,其代表的意義也是相通的。例如,處理器隊列長度這個指標,在Windows中的指標名稱是System\Processor Queue Length
,而在Linux系統中則需要看load averages
。
可能對於最后一項(測試設備)有些人不大理解,監控被測系統環境的相關硬件資源使用情況不就好了么,為什么還要關注測試設備本身呢?這是因為測試設備在模擬高並發請求的過程中,設備本身也會存在較高的資源消耗,例如CPU、內存、網卡帶寬吃滿,磁盤IO讀寫頻繁,處理器排隊嚴重等;當出現這類情況后,測試設備本身就會出現瓶頸,無法產生預期的並發壓力,從而我們測試得到的數據也就不具有可參考性了。此處暫不進行展開,后面我會再結合實際案例,通過圖表和數據對此詳細進行說明。
需要說明的是,性能指標之間通常都是有密切關聯的,單純地看某個指標往往很難定位出性能瓶頸,這需要我們對各項性能指標的含義了然於胸,然后才能在實際測試的過程中對系統性能狀況綜合進行分析,找出整個系統真正的瓶頸。舉個簡單的例子,壓力測試時發現服務器端CPU利用率非常高,那這個能說明什么問題呢?是服務端應用程序的算法問題,還是服務器硬件資源配置跟不上呢?光看這一個指標並不能定位出產生問題的真正原因,而如果僅因為這一點,就決定直接去優化程序算法或者升級服務器配置,最后也很難真正地解決問題。
結論要點2:性能瓶頸定位的重點在於性能指標的監控和分析。
引入性能測試工具
通過前面的講解,我們已經知道性能測試的主要手段是通過產生模擬真實業務的壓力對被測系統進行加壓,與此同時監控被測系統的各項性能指標,研究被測系統在不同壓力情況下的表現,找出其潛在的性能瓶頸。
那么,如何對系統進行加壓,又如何對系統的指標進行監控呢?這里,就需要引入性能測試工具了。
當然,我們也可以先看下在不借助性能測試工具的情況下,如何手工地對系統進行性能測試。
假設現在我們要對前面提到的搜索功能進行負載測試,驗證在20個並發用戶下搜索功能的事務平均響應時間是否在3秒以內。
很自然地,我們可以想到測試的必要條件有如下幾點:
- 20個測試人員,產生業務壓力
- 1個指揮人員,對20個人員的協調控制,實現並發操作
- 1個結果記錄人員,對每一個人員的操作耗時進行監控和記錄
- 若干資源監控人員,實時查看被測系統的各項性能指標,對指標進行匯總、分析
- 1個結果統計人員,對20個用戶各操作消耗的時長進行匯總,計算其平均值
可以看出,要通過人工來進行性能測試,操作上極為繁瑣,需要投入的資源非常多,而這還僅僅是一個非常簡單的場景。設想,如果要測試10000並發,服務器有好幾十台,顯然,這種情況下是完全不可能通過投入人力就能解決的。這也就是性能測試工具存在的必要性和誕生的背景。
性能測試工具的基本組成
當前,市面上已經有了很多性能測試工具,但不管是哪一款,基本都會包含如下幾個核心的模塊。
- 壓力生成器(Virtual User Generator)
- 結果采集器(Result Collector)
- 負載控制器(Controller)
- 系統資源監控器(Monitor)
- 結果分析器(Analysis)
原理結構圖如下所示:
對照前面手工進行性能測試的案例,不難理解,壓力發生器對應的是眾多測試人員,結果采集器對應的是結果記錄人員,負載控制器對應的是指揮人員,資源監控器對應的是若干資源監控人員,結果分析器對應的是結果統計人員。
其中,壓力發生器又是性能測試工具最核心的部分,它主要有兩個功能,一是真實模擬用戶操作,二是模擬有效並發。
然而,大多數性能測試工作人員可能都會忽略的是,當前市面上性能測試工具的壓力發生器基本都是存在缺陷的。
先說下模擬真實用戶操作。如果熟悉瀏覽器的工作原理,就會知道瀏覽器在加載網頁的時候,是同時並發多個TCP連接去請求頁面對應的HTTP資源,包括HTML、JS、圖片、CSS,當前流行的瀏覽器普遍會並發6-10個連接。然而,性能測試工具在模擬單個用戶操作的時候,基本上都是單連接串行加載頁面資源。產生的差異在於,假如頁面有100個資源,每個HTTP請求的響應時間約為100毫秒,那么瀏覽器采用6個連接並行加載網頁時大概會需要1.7秒(100/6*100
毫秒),而測試工具采用單連接串行加載就需要10秒(100*100
毫秒),兩者結果相差十分巨大。這也解釋了為什么有時候我們通過性能測試工具測試得到的響應時間挺長,但是手動用瀏覽器加載網頁時感覺挺快的原因。
再說下有效並發。什么叫有效並發?有效並發就是我們在測試工具中設置了1000虛擬用戶數,實際在服務器端就能產生1000並發壓力。然而現實情況是,很多時候由於測試設備自身出現了性能瓶頸,壓力發生器產生的並發壓力遠小於設定值,並且通常測試工具也不會將該問題暴露給測試人員;如果測試人員忽略了這個問題,以為測試得到的結果就是在設定並發壓力下的結果,那么最終分析得出的結論也就跟實際情況大相徑庭了。不過,我們可以通過保障測試環境不存在瓶頸,使得實際生成的並發壓力盡可能地與設定值一致;另一方面,我們也可以通過在測試過程中監控Web層(例如Nginx)的連接數和請求數,查看實際達到服務器端的並發數是否跟我們的設定值一致,以此來反推壓力發生器的壓力是否有效。
了解這些缺陷的意義在於,我們可以更清楚測試工具的原理,從而更准確地理解測試結果的真實含義。
性能測試工具推薦
經過充分的理論鋪墊,現在總算可以進入正題,開始講解工具部分了。
在性能測試工具方面,我重點向大家推薦Locust
這款開源工具。目前階段,該款工具在國內的知名度還很低,大多數測試人員可能之前都沒有接觸過。為了便於理解,我先將Locust
與LoadRunner、Jmeter這類大眾耳熟能詳的性能測試工具進行簡單對比。
\ | LoadRunner | Jmeter | Locust |
---|---|---|---|
授權方式 | 商業收費 | 開源免費 | 開源免費 |
開發語言 | C/Java | Java | Python |
測試腳本形式 | C/Java | GUI | Python |
並發機制 | 進程/線程 | 線程 | 協程 |
單機並發能力 | 低 | 低 | 高 |
分布式壓力 | 支持 | 支持 | 支持 |
資源監控 | 支持 | 不支持 | 不支持 |
報告與分析 | 完善 | 簡單圖表 | 簡單圖表 |
支持二次開發 | 不支持 | 支持 | 支持 |
通過對比,大家可能會疑惑,Locust
也不怎么樣嘛,資源監控也不支持,報告分析能力也這么弱,那為啥還要選擇它呢?
授權方式這個就不說了。雖然LoadRunner是商業軟件,價格極其昂貴,但是國內盜版橫行,別說個人,就算是大型互聯網公司,用正版的也沒幾個。
從功能特性的角度來講,LoadRunner是最全面的,用戶群體也是最多的,相應的學習資料也最為豐富。個人建議如果是新接觸性能測試,可以先熟悉LoadRunner,借此了解性能測試工具各個模塊的概念和功能,在此基礎上再轉到別的測試工具,也都比較好上手了。不過,LoadRunner只能在Windows平台使用,並且並發效率比較低,單台壓力機難以產生較高的並發能力,這也是現在我棄用該款工具的主要原因。
同樣地,Jmeter的並發機制也是基於線程,並發效率存在同樣的問題;另外,Jmeter在腳本編寫和描述方面是基於GUI操作,個人感覺操作比較繁瑣(這個因人而異),因此不是很喜歡。
那么,我重點推薦的Locust
有啥特別的地方呢?
如果從整體功能上來看的話,Locust
的功能的確比較單薄。不過,作為性能測試工具最核心的壓力發生器部分,卻是非常不錯的。拋開官方文檔的介紹,個人覺得最贊的有兩點。
首先是模擬用戶操作,也就是測試腳本描述方面。Locust采用Pure Python腳本描述,並且HTTP請求完全基於Requests
庫。用過Requests
的都知道,這個庫非常簡潔易用,但功能十分強大,很多其它編程語言的HTTP庫都借鑒了它的思想和模式,如果將其評選為最好用的HTTP庫之一(不限語言),應該也不會有太大的爭議。除了HTTP(S)協議,Locust也可以測試其它任意協議的系統,只需要采用Python調用對應的庫進行請求描述即可。
另外一點就是並發機制了。Locust的並發機制摒棄了進程和線程,采用協程(gevent
)的機制。采用多線程來模擬多用戶時,線程數會隨着並發數的增加而增加,而線程之間的切換是需要占用資源的,IO的阻塞和線程的sleep會不可避免的導致並發效率下降;正因如此,LoadRunner和Jmeter這類采用進程和線程的測試工具,都很難在單機上模擬出較高的並發壓力。而協程和線程的區別在於,協程避免了系統級資源調度,由此大幅提高了性能。正常情況下,單台普通配置的測試機可以生產數千並發壓力,這是LoadRunner和Jmeter都無法實現的。
有了一個不錯的引擎,外表裝飾簡陋點也都是可以接受的了。不過雖然Locust功能單薄,特別是在性能指標監控和測試報告圖表方面比較缺失,但是Locust的代碼結構清晰,核心代碼量也只有幾百行,可擴展性也非常不錯。換言之,Locust的可玩性(hackable
)極強,對於一個想深入挖掘性能測試工具原理的人來說,Locust
非常適合。
好了,Locust的介紹暫且到這兒,后續我會再對Locust的使用方法和二次開發進行詳細介紹,也算是彌補官方文檔的不足吧。