1、熟悉套用
這是整個性能過程最關鍵的步驟之一,毋庸質疑。
我們必須了解:套用的架構
以我熟悉的套用類型為例。了解了套用架構,我們才能知道,我們需要模擬的是:一般的html靜態檔案請求、一般的servlet和jsp請求、AJAX請求、還是遠程調用請求等。
我們必須了解:套用的功能邏輯
</b>
2、測試需求
我們得到的測試需求往往是這么描述的:這個系統能否支撐100萬的uv(每天登錄系統的人次)。
言下之意是:按照目前的硬體性能和數量,系統能否支撐100萬的uv。
然而,我們了解的是吞吐量、回響時間等指標
吞吐量:系統每秒能處理的請求數,這個指標從伺服器的視角,表征系統容量
回響時間:從請求發出到第一個位元組返回所需要的時間,這個指標從用戶的視角,表征系統回響速度。
那么,請問開發同事:能把測試需求轉化成我們熟悉的吞吐量和回響時間嗎?
......
答案常常是否定的
怎么辦:只能由我們根據經驗,把100萬uv轉化成一系列的指標。
回響時間:根據國外的一些資料,一般操作的回響時間不能高於3~5秒;重要操作,如結賬操作的回響時間不能高於15秒。
吞吐量:可以根據已經上線的類似產品進行估計。或者,採用80/20原則進行估計。我們經常使用的是80/20原則。
雖然已經有了回響時間和吞吐量指標,但是測試需求還是不明確的。
我們的測試目的是什麼?
是驗證當前硬體和軟體配置能否支撐100萬uv?是測試當前的硬體和軟體配置最多能支撐多少uv?是幫助開發尋找性能瓶頸?
答案往往是:都要!
根據我們的經驗,開發的需求往往是這樣的(當然開發一般不會說得那么詳細,^_^):
首先,請你們驗證能否支撐100萬uv。如果不能支撐,請找一下性能瓶頸。主要性能瓶頸解決後,請估計能支撐多少uv,如果不到100w,請估計要加多少機器。如果能支撐100萬,請再加壓,看看達到300萬uv的時候,系統的性能。這么一細化,需求基本明確了。
3、測試準備
測試準備包括測試客戶端機器準備、測試數據準備、測試腳本準備。
客戶端機器:要足夠,否則,如果瓶頸在客戶端,就無法評估服務端。要和伺服器保持網路通暢,否則,如果瓶頸在網路,也無法評估服務端。包括:網路頻寬要高於伺服器吞吐量、網路頻寬要穩定。
測試數據
如果被測功能涉及資料庫和高速快取,通常需要預設很大的數據量才能凸顯性能瓶頸,這通常是挺困難的一個環節。
如果是已經上線的套用,數據可以從線上拷貝得到;如果還沒有上線,那需要構造類似於線上的數據量。
比如,要測試群聊性能,我們首先需要註冊大量用戶;然後把測試用戶都加入到聊天群中。
測試數據準備的腳本,有時候比測試腳本本身還要多。
對於實在沒有辦法構造大數據量的情況,如果要測試高速快取,我們有時候會按數據量的比例減少高速快取,以使測試結果儘量準確。
測試腳本
Grinder腳本用jython實現
測試腳本的實現往往會花費比較長的時間
因為涉及到套用實現的細節,需要和開發不斷交流才能完成。這也是需要了解套用架構的原因之一。
關於sleeptime
基於真實模擬的考慮,sleeptime還是儘量按照真實時間,並給一定的偏差。
不過對於測試客戶端來說,sleeptime往往會引起很多客戶端測試執行緒的調度,浪費客戶端系統資源。
Sleeptime越小,客戶端能模擬的吞吐量就越大,所以,實際測試中,我們往往會把sleeptime設定為0。
4、測試執行
測試的執行中,需要監控測試客戶端和伺服器性能,監控伺服器端套用情況:
客戶端的系統資源(cpu、io、memory)情況
服務端的系統資源(cpu、io、memory)情況
伺服器的jvm運行情況
服務端的套用情況,看是否有異常
回響時間、吞吐量等指標
系統資源監控,linux下可以採用的工具有:vmstat、top、meminfo等。
JVM的監控,可以用jprofiler工具,linux下面的jmap、jhat等。
回響時間、吞吐量等,由grinder提供。
上述這些信息,一般在測試結束後,均需要歸檔整理,已備後續詳細分析
我們自己開發一套腳本,用於以固定的頻率獲取測試客戶端和伺服器的vmstat和top輸出、grinder的log,並從中截取有用信息保存,用於事後分析。
每次測試運行完以後,肯定會增加很多數據,需要考慮本次執行對數據量的影響,如果數據量的變化對後續測試會有影響,則需要清理數據。
5、測試分析
測試分析一般跟測試監控息息相關,在測試執行的過程中,用各種監控工具能看到系統運行的狀態,並及時發現問題。
常見的問題有:記憶體問題、有限資源競爭問題。
記憶體問題
從top中看tomcat的記憶體占用,這個是不準的,需要用專門的記憶體分析工具來查看。
工具:jmap,jhat,jstat,可以得到記憶體快照,得到堆記憶體的詳細信息。
垃圾收集配置會影響系統性能,如果記憶體塊生成和銷毀量很大,則能看到系統吞吐量隨垃圾收集呈現周期性的變化。
從理論上來說,JAVA會出現記憶體泄漏的情況,不過我們在被測試的套用中還沒有發現過這種情況。
但是,在某些系統架構下,記憶體會成為瓶頸問題。比如我們曾經測試過聊天系統,每個長連線需要占用5M記憶體,那么,一台10G記憶體的伺服器只能保持2000個長連線。
共享資源競爭問題
有限資源的競爭有很多,比如Service層的一個共享對象,比如資料庫連線,比如資料庫中的某一個使用頻率很高的數據表。
一個共享資源在一個時間點上,只能被一個執行緒獲得,其他執行緒必須等待,這就容易造成很多執行緒的timedwait狀態。通過jprofiler工具,能夠得到執行緒快照,並分析改進方法。
性能測試經驗交流——偶然性問題
跟一般的功能測試一樣,性能測試也會出現偶然性問題。
碰到這種問題,我們需要發揮測試人員的革命精神,追查到底。我們常發現的因素如下:
外部因素變化,比如,某幾次測試,有時候好,有時候不好,並沒有規律可循。最後發現原來是因為網路不穩定造成。請求返回變化。有時候第二次請求的內容取決於第一次的返回信息(也就是所謂的“關聯”),這種關聯一般通過string的parse實現,而這一般都不是很可靠,返回一旦變化,可能就會出錯。
套用伺服器如果是集群,一個用戶請求某一台伺服器能得到正確返回,但是如果換做另一個用戶,可能該伺服器並沒有該用戶的信息,所以返回錯誤。
性能測試經驗交流——客戶端並發
測試客戶端要模擬高並發,必然要啟動多執行緒,所以肯定也會存線上程並發問題。比如:
在做參數化的時候,存儲參數的數組就是一個共享對象。如果要使每個執行緒的每次循環都讀取不一樣的參數,那數組下標的更新需要注意並發問題。
比如,如果在腳本中要調用System.out,那么也需要注意這也是一個共享對象,如果調用System.out過多,會導致執行緒的等待,使客戶端性能降低。
性能測試經驗交流——測試人員
性能測試由於涉及面廣,對測試人員的要求就很高。我想,性能測試人員應該培養如下幾方面的能力:
如前所述,對套用架構的透徹理解。
溝通能力,測試進行過程中,一定要培養勤於跟開發溝通的意識,以提高工作效率。
解決問題的能力,在編腳本或者測試執行過程中,會碰到很多問題。首先是不要害怕,先考慮問題的可能原因,然後一步步定位、驗證。當然,這個過程,需要調試等經驗的不斷積累。