第 3 章 開發效能測試引擎
經過對 Java 多執行緒程式設計基礎和常用的功能的學習,我們已經具備了在效能測試之海自由翱翔的條件。但在出發之前,我們需要一款超級引擎,讓我們更快更絲滑擴充航海範圍。
本章我們要開發一款基於 Java 的效能測試引擎,透過效能測試常見測試需求的拆解、設計、程式設計實現,逐步完成這個目標。
在這個過程當中,我們會逐個實踐前兩章中所學到的知識點和技能,增強我們的熟練程度,為後面更具挑戰性的專案做好準備。
3.1 效能測試模型
對於新手效能測試工程師來講,效能測試模型可能比較陌生。因為常用的測試工具通常會將一部分封裝隱藏起來,暴露給使用者的就是執行緒數、使用者數、併發數此類概念。如果想從測試工具直接切換成為 Java 效能測試方式,一時間不太好適應。所以我們先從一個小故事開始本章的內容。
3.1.1 超市小故事
在開發效能測試引擎之前,先看一個小故事:
回到第 1 章中提到的 “小八” 超市,假設超市老闆 “小八” 擴大了規模,增加了收銀臺的人手,現在總計 8 條結賬通道,每個通道 1 個收銀員。
在設計新的收銀臺的時候,小八手上有兩種方案可供選擇。第一種是收銀臺佔地比較小,每個結賬通道都有單獨的排隊區域,有護欄與其他空間隔開,前面顧客結賬完成,後面顧客才可以進入收銀臺。這裡稱為排隊方案;二是收銀臺佔地比較大,將整個收銀臺和其他區域隔開,顧客可以先進入收銀臺,後選擇結賬通道。假設兩種設計,進入收銀臺的顧客只有兩種選擇:要麼將手頭的商品結賬帶走,要麼放棄購買這些商品,直接出超市。這裡稱為限流方案。
在業務高峰期,結賬的人依舊會很多。加入每個結賬通道結賬能力穩定,每位顧客結賬流程預計花費 2 分鐘,那麼每分鐘預計可以為 4 位顧客結賬。當每分鐘要結賬的顧客大於 4 位,我們來看看兩種收銀臺設計會發生哪些有趣的事情。
排隊方案:每個結賬通道都要排隊,新來的待結賬的顧客自然會選擇人數最少的結賬通道。如果顧客太多,等待的隊伍會越來越長。
限流方案:每分鐘會允許 4 位顧客會進入到收銀臺區域,新來的待結賬顧客自然會選擇空閒的結賬通道。如果顧客太多,大多數都會在收銀臺區域外等待進入收銀臺。
當收銀臺處理能力穩定,且與限流方案設定值一致,兩個方案設計除了收銀臺佔地面積以外沒啥特別大的區別,都是理想的狀態。
下面我們向這個穩定系統增加一個變數:打雷下雨了,一位收銀員要去回家收衣服,只剩下 7 位收銀員。
若是排隊方案,沒有收銀員的結賬通道會關閉,排隊的人會自行分散到其他隊伍中,此時收銀臺的結賬壓力依舊是每個收銀臺 2 分鐘完成一次結賬。若是限流方案,依舊按照 4 位每分鐘的速率讓顧客進入收銀臺,那麼收銀臺會逐漸無法承載壓力,最終會導致顧客在收銀臺發生嚴重擁堵。有顧客就會因為各種原因放棄此次購物,直接從無購物通道離開超市。
除了收銀員外,顧客也是不穩定因素,下面我們向穩定系統注入另外一個變數:加入顧客中有兩位老年顧客,他們相約來了一次大采購,東西多種多樣,自帶小推車。由於種種原因,實際處理這兩位顧客需要每人 10 分鐘。
若是排隊方案,那麼這兩人所在的隊伍結賬時間會明顯變長,不明所以的其他顧客依舊會按照隊伍長短選擇隊伍排隊。在這兩位顧客後面的排隊顧客,等待的時間就會變很長。若是限流方案,兩個人結賬時,兩條結賬通道就會被佔據較長時間,收銀臺會積累幾位顧客,最終當客流量減少到 4 位每分鐘之後幾分鐘內,收銀臺就不存在積累的顧客。
除此以外,兩種方案在應對多位顧客時存在差異。假如小八有兩位好朋友:小七和小九,兩人想約逛 “小八” 超市。兩個人各買了 120 塊錢的商品,但是小七隻帶了一張 100 元的紙幣,小九帶了兩張 100 元的紙幣。兩人商量了一下,決定採取這樣的策略:先讓小九結賬,然後小九把找零的錢借給小七 20 元。
這種場景如果是排隊方案,只需要讓小九排在小七前面即可。在限流方案中,兩人分配到的結賬通道不一樣,結賬順序也是無序的,所以無法保證他們策略中根本條件:小九在小七前結賬。那麼最壞的情況,小七先結賬,但是要一直等到小九結賬之後把錢送過來。這樣,小七所在通道在這段時間內都是無法給其他顧客結賬的。
假如小七和小九就是這兩位採購的老人,造成的情況會更加嚴重。在排隊方案,他倆排一對,但是在限流方案,最壞的情況,小七會阻塞某個結賬通道很長時間,導致收銀臺積累的使用者中著急回家收衣服的,就會選擇放棄購物。
我們再更加極端一點,假如小七和小九共享了 8 折購物卡。小九結賬時發現購物卡在小七哪裡。而此時小七也正收銀臺另外端翹首期盼小九送錢來付款。那麼兩條購物通道就會被堵住,再也無法給其他顧客結賬了。
排隊方案還有一個管理方面的優勢,可以針對結賬通道進行區分管理,提升整體的顧客結賬速率。例如可以設定電子支付快捷通道;設定自助結賬通道;設定優先結賬通道等等。透過流程最佳化,裝置專一化,降低平均購物結賬時間。而且排隊方案由於增加了護欄,可以有效降低有些投機顧客經常串對的麻煩。而限流方案的顧客進入收銀臺之後,再進行管理的難度和成本就會升高很多。
在這個故事中,收銀臺就是我們所測的服務,結賬的顧客就是客戶端的請求,顧客從進入收銀臺到離開超市的時間,就是請求的延遲或者說響應耗時。
如果顧客在進入收銀臺前擁堵排隊,則不會影響收銀臺處理單個顧客的耗時。若顧客在收銀臺內擁堵,則會增加等待耗時,但不會增加處理單個顧客的耗時,但在客戶端角度看,這兩個時間之和,即為響應耗時,會增加。
在小七和小九場景中,借錢的過程可以理解為兩個非同步任務,在進行多執行緒通訊或者說多執行緒協調之後,按照我們預期執行。最後的極端場景,兩個人都卡在了收銀臺,相互需要對方哪裡執行完結賬流程,導致誰也無法完成結賬流程,這就是 Java 多執行緒程式設計常會遇到的死鎖。
排隊方案和限流方式對應的就是我們本節的主題,分別對應執行緒模式和 TPS 模型。這裡的 TPS 指的 Task Per Second,跟常見 QPS(Queries Per Second)、TPS(Transactions Per Second)以及 RPS(Requests Per Second)同根同源,描述單位時間內執行的任務數量,在絕大多數場景可以相互替代。為了防止混淆,下面內容統一稱為 TPS。
3.1.2 執行緒模型和 TPS 模型
執行緒模型用於模擬併發訪問的一種測試設計方法,最常遇到的就是執行緒模型。它最小管理單元是執行緒,通常執行緒會迴圈執行某一段任務。效能測試設計者需要設計這段任務內容,交給執行緒去迴圈執行。最後透過對執行執行緒數的調整,來模擬不同效能測試場景的壓力。
用虛擬碼演示如下:
public static void main(String[] args) {
for (int i = 0; i < N; i++) {
new Thread() {
@Override
public void run() {
for (int j = 0; j < M; j++) {
executeTask();
}
}
}.start();
}
}
TPS 模型用於模擬不同請求速率的一種測試設計方法,也是效能測試常見的模型。他最小的管理單元就是任務,通常會有一個執行器,以設定的速率多次執行任務。效能設計者需要設計這個任務的內容,交給執行器按預期速率執行。最後透過對執行速率的調整,來模擬不同效能測試場景的壓力。
虛擬碼演示如下:
public static void main(String[] args) {
while (true) {
Thread.sleep(1000);
for (int i = 0; i < N; i++) {
executor.executeTask(task);
}
}
}
兩種測試模型在使用中各有所長,就如前文所說,執行緒模型更加容易實現,而且更方便進行管理。限於篇幅,筆者將基於執行緒模型進行後續內容的講解。對於 TPS 模型,將會在本章的末尾展示一個相對基礎版本的開發流程。希望各位能在完成本書學習之後,可以根據實際測試需求,豐富這個 TPS 模型壓測框架。
3.1.3 效能測試引擎展望
在最小管理單元的維度以外,還有針對效能測試用例執行過程中的靜態模型和動態模型。靜態模型指的是用例的壓力策略在執行前設定,例如遞增策略,最大壓力等,用例一旦執行起來,就無法改變壓力策略。而動態模型是可以在執行過程中根據服務壓力、監控指標等反饋資訊,靈活調整壓力值。設計這樣的效能測試引擎,可以當作完成本書內容實戰後,下一步的目標。實現這個目標,我們要擁有更強的多執行緒管理能力。如果思路再開啟一點,當單機無法滿足我們的效能測試需求,就會用到分散式效能測試。又是一座需要攀登的高峰。咱們山頂見!
書的名字:從 Java 開始做效能測試 。
如果本書內容對你有所幫助,希望各位不吝讚賞,讓我可以貼補家用。讚賞兩位數可以提前閱讀未公開章節。我也會嘗試製作本書的影片教程,包括必要的答疑。
FunTester 原創精華
【連載】從 Java 開始效能測試
- 混沌工程、故障測試、Web 前端
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go
- 白盒、工具、爬蟲、UI 自動化
- 理論、感悟、影片