背景介紹
在性能測試工作中,有時需要對業務系統所能支持的最大在線用戶數目進行評估。這與我們接觸最多的壓力測試不一樣,因為用戶在線時只是與服務器保持連接,並不一定對服務器有業務請求,從而對服務器不一定會產生壓力。然而,在線用戶數目並非可以無限增長,當在線用戶數目達到應用服務器(或者WebLogic等中間件,或者數據庫連接池等)的連接數設置的極限時,業務系統同樣可能會發生異常,出現新用戶無法登錄,或者老用戶被擠出系統,甚至業務系統宕機的情況。因此,對業務系統的最大在線用戶數指標進行測試是極其必要的。
現有一OA系統,需要測試其支持的最大在線用戶數目。已知當使用瀏覽器登錄該系統后,登錄用戶可持續地保持登錄狀態,即使長時間不做任何操作也不會自動退出系統;通過該OA系統的在線用戶數統計模塊可以詳細地查看到當前在線的用戶。
測試方法分析
為了測試被測系統所能支持的最大在線用戶數,需要不斷地使用新用戶帳號進行登錄操作,在此同時查看被測系統的在線用戶數目以及系統的響應情況。
在新增登錄用戶時需要注意,由於考察的是系統在正常情況下所能支持的在線用戶數目,而不是系統在並發壓力下的性能響應情況,因此登錄用戶時最好采用單個用戶或少量並發用戶(如兩個或三個)逐步登錄的形式,不同登錄批次之間最好能有一定時間間隔,務必使新增登錄用戶的操作對服務器產生盡可能小的業務壓力。
在新增登錄用戶的過程中,需要對被測系統的在線用戶數目進行查看,並着重關注以下幾個方面:
- 持續新增登錄用戶的同時,業務系統中的在線用戶數目是否相應地進行增長
- 持續新增登錄用戶的過程中,系統登錄操作是否產生連接超時的情況,事務的響應時間是否出現大幅度上升的情況,系統登錄事務是否出現失敗的情況(這需要在腳本中對登錄事務做檢查點設置)
- 持續新增登錄用戶的過程中,定期地在瀏覽器中手動刷新業務系統界面,查看業務系統是否出現不可訪問的情況(如內部服務器錯誤、宕機等)
相應地,測試腳本需符合如下形式:
lr_start_transaction("進入登錄頁面"); /*此處為進入系統登錄界面的腳本*/ lr_end_transaction("進入登錄頁面", LR_AUTO); lr_start_transaction("登錄系統"); /*登錄腳本--部分1*/
/*對系統登錄進行校驗 *若成功,即新頁面中包含"重新登錄",reLogin_Count>0 *若失敗,reLogin_Count=0 */ web_reg_find("Text=重新登錄", "SaveCount=reLogin_Count", LAST ); /*登錄腳本--部分2(包含檢查點內容部分)*/ //對系統登錄結果進行檢查 if (atoi(lr_eval_string("{reLogin_Count}")) == 0) { //登錄失敗 lr_error_message("登錄失敗!!!--UserName:%s",lr_eval_string("{UserName}")); //勾選Fail Open Transations on lr_error_message //當執到該lr_error_message時,"登錄系統"的Transaction失敗 } lr_end_transaction("登錄系統", LR_AUTO);
以上腳本調試成功后,通過檢查點函數及日志信息可以判斷出系統登錄操作已通過腳本回放成功完成。
理論上,只要VuGen采用不同帳號迭代運行該腳本,由於只是進行系統登錄操作而未進行系統注銷或退出操作,業務系統中的在線用戶數將持續增加。
然而在該OA系統中采用此方法時發現,雖然腳本成功運行,但業務系統中的在線用戶數並未增長。這說明LoadRunner與瀏覽器在訪問系統的過程中存在差異性。
腳本及場景設計
針對上面的問題,通過抓包工具Fiddler2對系統進行網絡流量抓包分析可知:用戶登錄系統后,在未進行任何操作的情況下,瀏覽器與服務器會定期(間隔30秒)進行通訊交互。如下圖所示:
這就解釋了為什么用戶在瀏覽器中登錄系統后可以長期地保持在線,而通過腳本成功地進行系統登錄后卻無法保持在線狀態;因為VuGen不會像瀏覽器那樣定期地與服務器進行通訊交互。
找出其中的差異后,我們便可在VuGen中用腳本模擬瀏覽器的定期交互功能,簡單的實現方法如下所示。
while(1){ web_url("userAction.struts", "URL=http://10.147.15.28:9001/userAction.struts?actionType=refreshDynaInfo", "Resource=0", "RecContentType=text/html", "Referer=http://10.147.15.28:9001/jsp/oa/infocomm/sms/sysShortMsg/sysShortMsgReflesh.jsp", "Snapshot=t4.inf", "Mode=HTML", LAST); web_url("userAction.struts_2", "URL=http://10.147.15.28:9001/userAction.struts?actionType=refreshDynaInfo&time=Mon%20Aug%2012%2015:42:04%20UTC+0800%202013", "Resource=0", "RecContentType=text/html", "Referer=http://10.147.15.28:9001/jsp/oa/infocomm/sms/sysShortMsg/reflesh.jsp", "Snapshot=t5.inf", "Mode=HTML", LAST); lr_think_time(30); //模擬瀏覽器與服務器30秒間隔的通訊交互 }
那么,將系統登錄腳本和循環函數都放入Action中,在VuGen中采用迭代運行的方式可行嗎?
雖然新增登錄用戶可以逐個進行,即使用VuGen通過Action迭代的形式采用不同帳號逐個地進行登錄操作。但由於用戶登錄后需要持續間隔地與服務器進行通訊交互才能保持在線,而單線程腳本運行至while(1)后便進入死循環,從而使得Action迭代無效。因此,采用VuGen進行Action迭代的方式是不可行的。
正確的做法是,在Controller中采用逐步加載的方式,使各個虛擬用戶獨立地運行,從而保證了各虛擬用戶登錄成功后保持在線狀態。
在Controller中的具體配置如下所示:
- Run Logic:Number of Iterations設置為1
- Think Time:Replay think time As recorded
- Continue on error:False
- Fail open transactions on lr_error_message:True
- Schedule:Initialize each Vuser just before it runs
- Schedule:Start 1000 Vusers -- 1 Vuser every 3 Seconds
- Schedule:Run for 5 minutes
通過以上配置,可以達到如下效果:
- 每個Vuser只登錄一次,然后定期30秒與服務器進行一次交互,保持在線狀態
- Vuser的登錄操作與定期刷新操作不會對服務器造成並發壓力,且符合真實業務場景
- 逐步緩慢地增加在線用戶數,當系統出現異常時即可查看到當前的在線用戶數目
測試方法優化
通過以上方法可以測試得到業務系統所能承受的“初略的”最大在線用戶數目。為什么說是“初略的”呢?因為該方法仍存在缺陷,主要體現在如下兩個方面:
- 該方法只適用於測試期間無他人使用系統的情況。如果測試期間同時有其他用戶登錄系統,或者系統中本身已存在在線用戶,則會造成測試得到的結果不准確。
- 該方法忽略了系統穩定性對在線用戶數的影響。舉例來說,也許逐步增加在線用戶數至500時,系統並沒有發生異常,但這並不意味着500個用戶長時間處於在線狀態時系統不會出現異常。
針對以上兩方面缺陷,可以做出如下改進:
- 在逐步增加在線用戶數的時候,定期(比如間隔3秒)查看業務系統自身統計的在線用戶數目,並以該數據為測試結果。
- 利用之前的方法測試得到業務系統“初略的”最大在線用戶數后,使系統長時間保持該數量的在線用戶數目,觀察系統在長時間運行期間是否會出現異常;若出現異常后,適當減少在線用戶數目后重復地進行測試,直到系統可以保持長時間地穩定運行為止,此時對應的在線用戶數目即為業務系統所能承受的最大在線用戶數目。
在本文提到的OA系統中,對鏈接[http://10.147.15.28:9001/countAction.struts?actionType=listOnlineUser] 進行請求可以返回得到當前系統在線用戶數目的統計信息。對應地,設計如下腳本,即可實現對系統實時在線用戶數目的查看。
Action() { web_add_cookie("USERORGID=db93a2M11f6719ff92Mf528764d624db129b32c21fbca0cb8d6; DOMAIN=10.147.15.28"); while(1){ lr_start_transaction("查看在線用戶數目"); web_reg_save_param("OnlineUsers", "LB=[在線用戶數/總用戶數:<font color=\"red\">", "RB=</font>]", "ORD=2", "Notfound=error", "Search=Body", LAST); web_url("countAction.struts", "URL=http://10.147.15.28:9001/countAction.struts?actionType=listOnlineUser", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf", "Mode=HTML", LAST); lr_output_message("當前在線用戶數目:%s", lr_eval_string("{OnlineUsers}")); lr_end_transaction("查看在線用戶數目", LR_AUTO); lr_think_time(3); } return 0; }
更多內容請訪問我的個人網站:http://52test.org/posts/Evaluate-Largest-Online-Users.html