Apache JMeter™ : 負載測試與效能測量的好工具

JMeter 是 Apache 組織下一套百分之百由 Java 來開發的 open source 軟體,主要設計用來測試軟體行為在負載較重的情況下是否能夠正常運作且不出錯,以及測量軟體在給定負載量(or給定壓力)下的效能表現。

時下的軟體架構,隨著硬體架構主流轉為分散式架構設計,也開始要求盡量利用並行程序的方式來盡可能的榨取硬體的效能,利用硬體本身的多個 CPU 及單 CPU 擁有多個核心設計,進而達成大量且高速運算的要求,來符合今日大數據時代的需要。這樣的軟體架構要求,自然讓人必須改變從前設計軟體的舊思維,從而擁抱今日因多執行緒需求而生或再度崛起的各式語言,如 Scala、Erlang、Haskell…等這樣的 functional program language。

似乎有點離題了,JMeter 能夠 理論上模擬無限多個執行緒來同時存取你的服務(如呼叫API,取得網頁內容等),測試在各種極端環境下,軟體行為是否都能夠正確的執行,協助我們找出未來服務正是上線後可能碰到的問題,以及測量在該極端環境的下,服務的效能是否夠好,能夠讓使用者感覺不到那一片的存在一般。

舉些簡單的例子的話,就是每當五月天、阿妹、江蕙要開演唱會,數萬人同時間湧入售票系統搶票的時候,就在這短短的幾分鐘內,對於售票系統的負載可不是鬧著玩的,這時有沒有事前先做好這方面的測試,從事後鄉民的謾罵聲便可一窺一二了!


系統需求

由於 Apache JMeter 是由 100% 的純 Java 所寫成的,這意味著 JMeter 可以在幾乎所有的平台上執行,我們所需要的就只有 Java 6 或更新版的 JRE,一旦安裝好 JRE 之後,就可以著手開始我們的測試了。


安裝 JMeter

JMeter 並沒有所謂的安裝程序,所需要的只有從 Apache JMeter 的下載頁,下載 Binaries 分類下的 zip 或是 tgz 檔後解壓縮,在 bin/ 資料夾下找到 jmeter (Linux / OS X) 或是 jmeter.bat (Windows) 執行即可開啟 JMeter 的圖形化使用者介面 (GUI)。

筆者的作業系統環境是 Mac OS X,在這邊會發現解壓縮後的 jmeter 是沒有執行權限的,如下:

// 缺少了 x,表示沒有執行的權限
-rw-r--r--@  1 Cloud  staff   5594 Nov  5 19:51 jmeter

記得使用 chmod +x jmeter 補上執行的權限。


使用 JMeter

如安裝 Jmeter 章節所述,直接使用 jmeter 或是 jmeter.bat 開啟 Jmeter 的圖形使用者介面,介面如下:

建立第一個測試

通常第一次打開 JMeter 大家都會覺得不知所措,不知道該怎樣開始建立一個測試方案(相信我,因為筆者也是這樣),不過如果您已經知道怎麼樣建立的話,大概也不會閱讀這篇文章吧!

首先我們需要先建立一個 Thead Group,一個 Thread(執行緒) 就代表一個使用者,所以我們建立了一個使用者的群組,我們可以設定使用者的人數,來模擬同時存取受測服務的使用者人數。

Test Plan 上點右鍵, Add → Threads (Users) → Thread Group

這邊我們設定會有 10 個使用者來存取 (測試) 我們的服務:

這裡的 Ramp-Up Period (in seconds) 指得是「在幾秒內達到所設定的使用者人數」,可以讓受測服務一開始不會接受到太過巨量的 Requests

接下來我們希望模擬 每個使用者,都會對我們的服務存取一定的次數
Thread Group 上點右鍵, Add → Logic Controller → Loop Controller

設定 Loop count (迴圈/重複執行次數)為 100 次

設定完 Thread GroupLoop Count 後,也就等於控制了對受測服務所發出的 request 數量,這邊作個簡單的計數的話就是:

10 (Users) * 100 (Loop Count) = 1,000 (Requests)

也就是我們的服務將接受 1,000 次 requests 的測試。

目前我們還沒設定任何的受測項目,所以接下來我們必須要建立受測項目。在建立受測項目之前,考量到一個測試專案可能會同時測試多個譬如 API(s) 之類的服務,故我們先建立一個 HTTP Request Defaults。在 Loop Controller 上點右鍵, Add → Config Element → HTTP Request Defaults

新建立的 HTTP Request Defaults 顧名思義是用來設定一些預設的 HTTP Request 參數,這些參數會用來作為往後各個受測項目的預設參數,有點像是 Class 的父類別的味道在裡面:

這邊將一些比較重要的參數設定用紅框標示出來,分別為:

  • Server Name or IP:受測服務伺服器的 HOST Name 或是 IP 位置
  • Port Number:受測服務所使用的 port
  • Protocol:受測服務所使用的通訊協定,譬如 httphttps 等,JMeter 提供了多種 protocol 的支援

接著,建立最終的受測項目 (Request)。在 Loop Controller 上點右鍵, Add → Sampler → HTTP Request

由於我們使用了 HTTP Request Defaults 幫我們設定了一些既定的 Http Request 的設定,所以在這邊我們只需要針對一些會變動的地方作設定即可:

  • Path:完整的服務路徑
  • Method:HTTP Request 的方法
  • Parameters:參數設定,如使用 GET/POST 時,受測服務所要求傳入的參數,便在此設定

填入詳細路徑與參數後,我們的第一個測試項目就算是完成了!不過慢著,既然 JMeter 是用來「測量」服務的負載以及效能的表現,我們還必須建立有關測量這方面的相關設定。

建立測量圖報表

我們一樣在 Loop Controller 上點右鍵, Add → Listener → Graph Results 加入圖形化的測量結果:

另外,再次在 Loop Controller 上點右鍵, Add → Listener → View Results Tree 加入 View Results Tree 來記錄每一筆 Request 的結果

建立完這兩個量表之後,我們就可以開始來執行所設定的測試並且測量服務的效能表現啦!不過再開始測試之前,記得先把目前的測試項目的腳本存檔,這樣 JMeter 才有辦法根據腳本的內容來執行測試命令!

JMeter 的圖形化使用者介面只是一個方便開發者或使用者設定測試腳本以及視覺化測試回饋的工具而已,實際測試程式的執行還是都是透過 CLI 的命令來做的,所以一個方便我們編寫腳本的工具可以說是大大降低了學習曲線呢!

執行測試

再開始執行測試之前,先介紹一下 JMeter 的導覽列 (Navigation Bar),這邊標示出筆者四個常用的功能,如下圖:

  1. 測試腳本存檔
  2. 執行測試腳本
  3. 停止執行測試腳本
  4. 清除測試結果

將測試腳本存檔後,我們就可以藉由導覽列的執行鈕,或是透過快捷鍵 cmd + R 來執行編寫好的測試腳本。

從上面的 Graph Result 圖表中,我們可以看到幾個重要的數據:

  • No of Samples:記錄我們送出多少 Request 的欄位
  • Average:記錄每筆 Request 回應時間的平均值
  • Deviation:記錄整個測試過程中的回應時間的偏差值,越接近 0 表示服務越趨於穩定
  • Throughtput / minute:記錄了每分鐘我們的服務能夠回應/處理多少數量的 Request,越大表示受測服務的反應時間越短,效能越棒!

透過 Graph Result 圖形化測試結果的好處就是,我們可以很容易的觀察到,在測試腳本執行的過程中,是否有某些 Requests 的回應與大部分的 Requests 的回應不同(譬如 Timeout 或是 Request Failed 之類的回應),如果有的話在圖形上就會很容易呈現出一個不連續的斷點或是奇怪的曲線走向。

從上面的 View Result Tree 中,我們可以看到詳細的 Request 內容:

  • Request:測試腳本所送出的 HTTP Request 內容
  • Sampler result:該 Request 的一些 Metadata、Header 等等的資訊
  • Response data:所取得的回傳資料,這邊以 GET 本部落格的 Android: Tools Attributes 文章為例,故回傳的是該文章的 HTML 內容

我們也可以透過 View Result Tree 中的下拉式選單來選擇我們想要的檢視方式(如上圖),譬如說我們今天測試的是 API,那麼大多數我們會利用 JSON 來傳遞資料,這時就可以透過選單切換至 JSON 來檢視 JSON 的結構及數值是否正確;或是也有可能測試的是某個走 SOAP protocol 的服務,我們可能會想要利用 XML 來檢視回傳的資料內容,以此類推。

既然我們這邊的測試內容是取得本部落格的 Android: Tools Attributes 文章的內容,我們切換到 HTML 這個檢視模式來瞧瞧:

悲劇啊!怎麼那麼醜~看來 JMeter 應該沒有完整的下載 CSS 樣式吧?不過作為一個負載測試與效能測量的工具來說,pretty rendering 應該不是 JMeter 所著重的點才是;而是能夠確切的在我們指定的條件下送出我們所設定的 request 數目以及蒐集測量結果。

寫到這邊,讀者理應已經可以用 JMeter 開始簡單地測試各種從你們腦袋中蹦出來的測試方案,透過上線前擬定與執行各種嚴格條件的測試,確保在服務上線後不會有因為服務太熱門而導致服務不能穩定運作的狀況。

參數設定

上面的例子中,我們示範了最簡單的 Request 請求,甚至連參數都沒有!但是現實中,這樣的例子或許是少數,大部分的 request 都是帶有參數的,那麼,在 JMeter 中,我們要如何設定參數呢?

先前我們曾提到在 Http Request 有個設定參數的地方,還記得嗎?

我們可以透過 Add 按鈕來新增我們的參數:

是不是超級容易呢?增加所帶的參數就是這麼的簡單容易啊!

透過 CSV 檔案提供參數數值

雖然設定參數真是簡單到一個不行,但也因為簡單到一個不行,所以無法透過設定簡單參數的方式達到「每一個 request 都具有不同的參數數值」的要求,這個時候,我們必須透過外部的檔案來提供參數的數值,最直覺的方式就是 CSV 檔啦!(一般的使用者都可以輕鬆地透過 Excel 的拖曳功能,來產生大量不同的數值欄位),我們就來看看要怎麼樣使用 CSV 檔案的欄位來做為參數的數值吧!

首先我們必須先在 HTTP Request 上點右鍵,Add → Config Element → CSV Data Set Config 來加入 CSV 檔案的對應:

讓我們來看看這個 CSV Data Set Config panel 在設定上需要注意什麼地方:

  • Filename:CSV 檔的檔案路徑,注意:這邊的路徑是指相對於測試腳本位置的相對路徑,別設定錯囉!
  • File encoding:如果你的 CSV 檔案用了特殊的編碼的話,記得在這邊設定
  • Variable Names:設定變數名稱用來作為參數數值的 Placeholder,之後取得的數值會透過該變數帶入(若你的 CSV 檔案有多個欄位,分別提供不同參數作為數值來使用的話,請使用逗號分隔)
  • Delimiter:CSV 檔案的分隔符號,一般來說沒意外的話都是以 , 分隔的,故不需做修改

其他設定在這邊就不多加詳述,請參考 JMeter 官方的文件。然後回到我們的 HTTP Request 中的參數設定,在這邊我們加上參數oid,這個參數的數值在每個 request 中都必須是不同的,因此我們透過 CSV 檔案的方式來提供,參數設定如下圖:

注意到我們在 HTTP Request 的參數設定中,參數 oid 的數值使用了 ${oid} 這個 placeholder,這個 ${variable_name_set_in_csv_data_set_config} 就是稍早在 CSV Data Set Config 中,針對各欄位所設定的變數名稱,在測試腳本執行時期, JMeter 會自動的將 CVS 中每列的數值透過 Placeholder 給填充進去,藉此達到讓每個 request 的 oid 參數都帶有不同的值。


心得

測試時,務必確保服務的每個環節都進行過樣本數夠大且條件夠嚴苛的效能測量,這有助於:

  1. 找出系統架構是否有設計不良的地方,導致整個系統可能因為一處設計上瑕疵(譬如採用的資料庫與資料庫結構),而大大的降低了服務的效能與強健度
  2. 找出系統的效能瓶頸位於何處,有時可能是合作夥伴的服務有效能上的瓶頸,這時我們該思考是否有其他的方式來克服此一困難,或是我們不必花這麼多資源在現有的設備上,改提供一種「有限能力」的服務即可。(說得直白一點,合作夥伴就是讓你的服務這麼卡的元兇,你何必花大錢在高規格的EC2主機上試圖改進效能,根本不可能嘛xD)

JMeter 的功能當然不只本文所敘述的這麼陽春,JMeter 在單機上可能因為硬體設備,如記憶體容量的限制等條件,而限制了 JMeter 所能夠發出的最大請求數數量,針對這點,JMeter 也能夠透過 Master/Slave 這樣的主從式架構來解決,有時間的話在另行撰文介紹。關於 JMeter 的妙用,我想多多參考官方的文件應該就能駕輕就熟,筆者的目標是透過撰寫 Bash Shell Script 來自動化整個包含參數數值產生的測試流程,當然這就不是 JMeter 的圖形使用者介面能夠達到的,而是必須透過 CLI 的方式來完成。

本文謹獻給我的同事 Eddie & Jack,願 2015 年的你們一樣關關難過關關過,也可以利用這個工具助你完敗目前市面上爆爛的售票系統!Peace!

comments powered by Disqus