摘要:
在大型的應用項目中,核心業務系統的壓力測試是一個重要的環節。在系統上線之前,或者在應用的開發過程過都是必需的。本文結合一個實際的大型項目,對其壓力測試方案的設計和具體實施中的一些考慮和遇到的問題進行了分析和研究,提出了一種比較通用的基于系統真實數據的測試方法,并詳細討論實現方法,進而討論了相關的要注意的問題。
文章目錄:
問題的引出
TUXEDO作為一種成熟的事務處理中間件,一般用于大型系統的業務處理。在這樣的系統中,一般客戶端請求的并發數很大,而且對實時性要求很高,需要在規定的時間內處理完一個事務,并返回結果給用戶。而且對系統的穩定性要求也很高,一般都要求7x24運作。為滿足以上要求,除了系統各部分有良好的設計、編碼和測試外,還需要在上線前對系統的處理能力、極限容量等做一個測試和評估,以便獲得關于系統的更真實的性能情況。這種測試是從外部來觀察系統的整體情況,比起某個部分的性能評比更有實際的意義,而且使得項目開發人員和以后的運行維護人員對系統的整體性能有一個具體的認識,便于調整和日后的維護。
接下來筆者以自己參與開發的一個大型電信項目為依托,結合自己設計和實現的TUXEDO服務器壓力測試工具來分析一下壓力測試中的一些方法和過程,以及要注意的問題。
壓力測試必須有一定數量的并發客戶端。為了測試,準備大量的PC,并在每一個上裝一個client程序是不現實的,而且難以做到真正的并發,也不利于客戶端數目不斷變動的大量的測試,因此我們采用了軟件模擬客戶端的方法。對TUXEDO服務器而言,每一個客戶端就是一個和它通信的進程,所以需要多少客戶端簡單的說就是開多少調用服務的進程,這個在OS的支持下是很容易實現的。這里有一個問題需要討論,一般會想到用一個現成的壓力測試軟件來做。實際中發現,這種方式是有問題的。一般壓力測試工具的方法是截獲一個client到server的調用數據包,分析其中的數據,然后將一些數據進行參數化,例如一些ID等,然后生成一個可以產生大量并發的同類數據包的腳本,運行該腳本就可以進行壓力測試,當然其中包括了很多度量。對于某些應用,例如新開戶等,這種方法可以工作得很好,但是對于那些對數據真實性要求很高的服務,這種方法就難以實現。例如筆者做的壓力測試中涉及的主要TUXEDO服務——用戶帳單的查詢和銷帳處理。由于用戶的電話號碼或者帳號等通常是不連續的,中間有很多的空洞,而且銷帳的服務是依賴于查詢的返回結果的,如果不是數據庫中一條真實的可以做銷帳處理的帳單是不能成功的完成銷帳流程的。這對構造數據帶來很大的難度,而且那種構造出來的數據和實際的情況會有很大的差距,因為那可能被數據庫的緩沖等進行優化,不能反映真實的性能,而最好的辦法當然是用實際數據庫中的真實數據。當然,這種真實也可以有一定的擴充,下面會涉及到關于壓力測試數據的準備問題。通過上面的討論,我們知道在一些實際的系統中,讓了解系統應用的人自己動手來做壓力測試很多時候是一個更好的選擇。不必擔心這個工作的復雜性,下面我們就相關的問題開始詳細的分析。
1.模擬出指定數目的客戶端。
開多進程需要OS的支持,下面以UNIX為例給出了一個實現的代碼。需要特別注意的是后面注釋有*的那一行代碼,該行的意義是在子進程(pid=0,fork對父進程和子進程有不同返回,參考[1])中不再執行該循環。在UNIX中,子進程從fork()的下一句開始執行。如果沒有上面那一句,新開出的子進程發現滿足for循環的條件將繼續執行循環,開出新的它自己的子進程,這樣會產生復雜的進程樹,可以計算那樣得出的子進程的數目為。為更好的控制客戶端的數量和保持進程相互關系的簡明,我們通過上面那一句使得進程的關系只有兩層,而且進程數目就等于P_NUM。
for ( i = 0; i < P_NUM; i++ ) //P_NUM為要開的進程數,也就是模擬客戶端的數目 { if ( (pid = fork()) == 0 ) break; //* if ( pid < 0 ) exit(0); } if ( pid == 0 ) //子進程代碼 { child_process(); } if ( pid ) //父進程代碼 { … }
通過上面的代碼,我們對客戶端的產生有了清楚的認識,在進程創建后,父、子進程分道揚鑣,開始各自的壓力測試之旅。
2.系統真實數據的獲取和傳遞
上面我們討論了真實數據對壓力測試結果可信程度的重要意義,現在的問題是如何設計一個獲取數據庫中數據并傳遞給調用服務的客戶端的數據流程。這個前提是我們要對被測的服務比較熟悉,了解它的大致處理流程和涉及的數據。
以帳單的銷帳為例,它需要銷帳方式、操作員工號、涉及的每條帳單的部分信息,這些信息可以由用戶的輸入和帳單查詢服務來得到。用戶的輸入我們可以在測試程序中設定,帳單的信息我們可以在之前調用查詢服務來獲取,因為銷帳總是在帳單查詢之后進行的,所以這個流程也是符合實際情況的。接下來的問題是帳單的查詢也是需要有效的輸入參數的,我們如何獲取這些數據呢?答案自然還是從數據庫中來。在真實系統中,帳單查詢是前臺接收到用戶請求,根據用戶的電話號碼等來查詢,在這里我們可以一次從數據庫中取出一批有帳單的用戶的信息(電話號碼等)來作為查詢的輸入條件。因為這個取編號的操作不是真實系統應有的開銷,所以我們可以在模擬客戶端開始運行之前完成。
至此,我們對數據的流程有了一個大概的認識。下面就是具體實現的問題了。編號是一次性取出的,而且根據測試需要其數量是可以預先知道的,我們可以將其保存在一個大的數組中,后面我們可以看到用這種數據結構的好處。
獲得了最基本的輸入,我們就可以開始壓力測試程序內部的數據流的設計了。
3.父、子進程的工作及數據通信方式
為保證并發,每個client進程都應該是獨立的,而且自身運行必須維持一段時間,這樣才能在TUXEDO服務器一端產生大量穩定的連接,進而產生調用,形成壓力。每個client進程的工作是得到一個編號,調用帳單查詢的服務獲取對應用戶的帳單,然后整理返回結果,加入操作員信息,再調用銷帳服務,獲得返回結果。每個client重復上面的過程,重復的次數作為一個參數n,這個是可以配置的,P_NUM * n 就是需要準備的編號的數量,這兩個參數需要結合測試的要求來選擇,通過改變他們可以進行多次不同的測試。
父進程的工作是獲取編號到數組中,然后產生P_NUM個模擬的客戶端,接下來它要給這些client分發數據,使得每個client可以持續運行。上面提到的編號數組相當于一個糧食倉庫,父進程要給子進程穩定的糧食供應,使其不致于因為“饑餓”而導致停頓。分析這里的數據,還有一個特點就是每個數據都是一次性的,不能再次使用。父進程通過數組的下標可以很容易的保證數據的不重復,這也體現了數組在這種情況下的簡潔高效。在父進程輸送數據給子進程的過程中,我們考慮后采用了隊列來實現,因為要保證每個子進程獲得的數據都是唯一的,而且每個子進程最好能從一個已知的地點獲取數據,這樣便于子進程數目的伸縮,而隊列恰好能滿足這些要求。由于隊列的訪問是有控制的,大量的子進程去取可能會造成等待,但是實際測試中我們發現這種影響是很小的,在客戶端數目為1000左右時仍不明顯,因而我們可以認為隊列是高效的。當然這里的問題也可能有更好的解決的辦法,隊列相對而言比較簡單和清晰。
壓力測試程序必須返回相應的結果,例如每個調用所耗的時間等等,這里不詳細說明,可以根據實際的需要安排相關的代碼來實現。
4.測試系統的示意圖
通過以上幾個方面的討論,我們對壓力測試程序的主體和數據的流程有了認識。以上也是該壓力測試方案不同于一般的壓力測試工具的地方。下面我們通過一個示意圖來給獲得一個感性的認識。
5.TUXEDO參數的調整
為配合壓力測試,TUXEDO服務器的一些參數需要進行相應的調整。涉及的主要參數是以下幾個:
MAXACCESSERS
最大訪問者的數目,這個包含多種類客戶端,大于P_NUM * 1.2(參考值)。
MAXWSCLIENTS
最大workstation client的數目,要求大于P_NUM。
WSL -m 2 -M 30 -x 10
WSH的數目和每個的并發處理能力。m為WSH的初始數目,M為WSH的最大數目,x為每個WSH可以接受的WS client的連接數。要求M * x > P_NUM。
以上是幾個主要的參數,由于連接數要受到license的限制,一般正式license中USERS不會很大,所以進一步的測試可以找一個USERS數目較大的試用license。關于TUXEDO的配置可以參考相應的文檔,這里不詳細說明。
6.測試數據的生成
由于壓力測試是要長時間的運行的,而且要測試很多次,包括不同的配置的情況下,所以真實的數據往往是不夠用的,而上面的測試方法要求數據都是在數據庫中真實存在的。針對這個問題,我們可以根據真實數據生成一些滿足要求的數據,F在一般的DBMS都支持INSERT … SELECT 語句,通過下面的方法可以產生大量的可用數據。
假設某一個表的一條記錄以RECORD_ID為主鍵,max(RECORD_ID) = M;通過下面的SQL語句:INSERT INTO table_name(RECORD_ID, …) SELECT RECORD_ID+M, … FROM table_name; 就可以使表中的記錄數double。依此類推,可以使數據量指數級的增長,而這些數據除了一些標識性的ID不同,其他都是以真實數據為藍本的,比純粹由測試程序制造出的數據能更好的滿足TUXEDO服務程序的要求,實際的測試也證明了這一點。而且通過該方法,結合對業務增長的預測,我們可以模擬系統運行了某一段時間之后的性能情況。
7.測試的延伸
以上主要是針對TUXEDO服務程序的測試,其實以上的測試方法可以用來進行其它方面的測試。這里列舉兩個主要的方面:
一是TUXEDO的一些負載均衡機制,通過大量clinet的并發操作,可以分析針對具體的硬軟件配置情況下,那種配置是最合適的。
二是分析TUXEDO和數據庫的連接方式。目前TUXEDO連數據庫主要有XA和server直連兩種方式。以上方法也可以對這兩種方式的實際效果進行評估。
8.要注意的問題及可以改進的地方
為了使壓力測試的結果能更接近真實的情況,有一些問題需要注意。
首先壓力測試和真實的情況有一個很大的分別是所有的客戶端都是在一臺機器上,而不是真實環境下的分布在很多的PC上,這樣對測試程序所在的機器的性能就有一定的要求。實際測試中我們采用了一臺和主機性能接近的UNIX小型機,在模擬客戶端數目較大時仍能滿足要求。若沒有高性能的測試機可用,可以分布在幾臺機器上,同時運行測試程序。
另外操作系統的一些核心參數也需要考慮并做相應調整,比如一般UNIX系統中隊列的容量默認為16KB,不是很大,還有一些IPC的值對TUXEDO性能也有一定的影響,因為TUXEDO使用了Q、SEM、SHM等IPC通信機制,關于這個,TUXEDO官方文檔中有詳細的講解。這些可以和系統管理員一起來進行調整。
上面提到的測試程序內部數據流也可以用其它的方式實現。數據庫服務器的一些相關參數也可以做相應的調整,使得主要壓力集中在TUXEDO服務器,便于觀察其在不同負載下的表現,以及在負載非常重的情況下的穩定性和容錯性,包括自我恢復的能力。
小結:
筆者根據上面提到的思路和方法為所在的項目進行了長時間的壓力測試,并給項目經理和系統管理員提交了正式的壓力測試報告,使得大家對系統的一些整體性能有一個比較客觀的認識和評估,為滿足設計要求并順利通過驗收打下了基礎,獲得了比較好的效果。該測試程序通過簡單的配置和修改又被用于報表等其它的服務模塊。
本文是筆者根據上述實踐總結歸納出的一些方法和思路,并指出了需要注意的問題,希望能給其他系統的設計和開發人員在面對相關問題的時候提供一些參考。
文章來源于領測軟件測試網 http://www.kjueaiud.com/