引言
當(dāng)我們做Web系統(tǒng)性能測試方案時(shí),壓力模擬工具的選擇通常是一個(gè)繞不開的環(huán)節(jié)。對于大部分互聯(lián)網(wǎng)公司的業(yè)務(wù)規(guī)模和測試資源投入,JMeter這個(gè)老牌開源性能測試工具能夠滿足大部分測試需求,它也可能是世面上書籍、博客教程豐富程度僅次于LoadRunner的性能測試工具。然而當(dāng)我們的場景需要模擬的并發(fā)用戶數(shù)以千為單位時(shí),使用JMeter的成本越來越大,甚至超出我們掌握的資源。此時(shí),我們開始尋找更低成本的方案,而Locust,為這樣的方案帶來了一種可能。
簡介
Locust是開源、使用Python開發(fā)、基于事件、支持分布式并且提供Web UI進(jìn)行測試執(zhí)行和結(jié)果展示的性能測試工具。而它之所以能夠在資源占用方面明顯優(yōu)于JMeter,一個(gè)關(guān)鍵點(diǎn)在于兩者模擬虛擬用戶的方式不同,JMeter通過線程來作為虛擬用戶,而Locust借助gevent庫對協(xié)程的支持,以greenlet來實(shí)現(xiàn)對用戶的模擬,相同配置下Locust能支持的并發(fā)用戶數(shù)相比JMeter可以達(dá)到一個(gè)數(shù)量級(jí)的提升。
Locust使用Python代碼定義測試場景,目前支持Python 2.7, 3.3, 3.4, 3.5, 和3.6。它自帶一個(gè)Web UI,用于定義用戶模型,發(fā)起測試,實(shí)時(shí)測試數(shù)據(jù),錯(cuò)誤統(tǒng)計(jì)等,在最新未正式發(fā)布的v0.8a2(當(dāng)前最新發(fā)布版本v0.8a1),還提供QPS、評價(jià)響應(yīng)時(shí)間等幾個(gè)簡單的圖表。


本文不會(huì)介紹Locust最基礎(chǔ)的部署、運(yùn)行等Quick start式內(nèi)容,這部分內(nèi)容請直接參照官網(wǎng)Quick start或者搜索Locust入門的博客,本文主要介紹一些目前網(wǎng)絡(luò)上還比較缺少的,真正要用Locust來做Web系統(tǒng)性能測試時(shí)通常需要用到的內(nèi)容或者可能遇到的問題
v0.8a2<a id="a2"></a>
如前文所說,當(dāng)前官方最新發(fā)布的版本為v0.8a1,還不包含圖表特性,而在官方Github上已經(jīng)在v0.8a2完成了圖表特性的合并,想要提前體驗(yàn)可以直接從Github獲取master分支的代碼,覆蓋`[PythonHome]\Lib\site-packages`中的locust目錄即可。
指定Web host
在Linux系統(tǒng)多網(wǎng)卡情況下,Locust自動(dòng)選擇網(wǎng)卡時(shí)可能會(huì)遇到error: [Errno 97] Address family not supported by protocol錯(cuò)誤,此時(shí)可以通過直接指定web host來解決問題,使用選項(xiàng)--web-host來指定可用的地址,例:
locust -f xxx.py --web-host=127.0.0.1
locust -f xxx.py --web-host=192.168.1.2
locust -f xxx.py --web-host=localhost
斷言
當(dāng)我們沒有自定義斷言時(shí),測試請求結(jié)果的狀態(tài)(success/fail)取決于Http請求是否有異常出現(xiàn),而在對我們的Web系統(tǒng)實(shí)施性能測試時(shí),當(dāng)我們需要更準(zhǔn)確的業(yè)務(wù)成功率數(shù)據(jù)時(shí),就需要通過對響應(yīng)狀態(tài)碼、Response body等數(shù)據(jù)進(jìn)行校驗(yàn)來給出結(jié)果,此時(shí),可以通過ResponseContextManager來實(shí)現(xiàn)。首先在場景代碼的發(fā)起請求參數(shù)中通過catch_response=True來捕獲響應(yīng)數(shù)據(jù),然后對響應(yīng)數(shù)據(jù)進(jìn)行校驗(yàn),最后使用success()/failure()兩個(gè)方法來標(biāo)識(shí)請求結(jié)果的狀態(tài)。例:
from locust import HttpLocust, TaskSet,task
class UserBehavior(TaskSet):
@task(2)
def foo(self):
with self.client.get("/", catch_response=True) as response:
if response.status_code == 200:
response.success()
@task(1)
def bar(self):
reqBody = '{"username":"ellen_key", "password":"education"}'
with self.client.post("/login", reqBody, catch_response=True) as response:
if response.content == "":
response.failure("No data")
class WebsiteUser(HttpLocust):
task_set = UserBehavior
host = "http://foo.bar.com"
min_wait = 0
max_wait = 0
Json解析
Json作為一種輕量級(jí)的數(shù)據(jù)交換格式,以及被如今的互聯(lián)網(wǎng)系統(tǒng)廣泛采用。上一節(jié)的示例中,我們使用content獲取完整的響應(yīng)內(nèi)容,實(shí)際測試實(shí)施中,對于動(dòng)態(tài)的響應(yīng)結(jié)果,可能更多的采用校驗(yàn)關(guān)鍵字段的方式對于Json格式的響應(yīng)數(shù)據(jù),要獲取特定字段的值,可以直接使用內(nèi)置的Json解析實(shí)現(xiàn),例:
對于如下的響應(yīng)結(jié)果:
{
'code':0,
'data':[
{
'id':123
}
]
}
可以通過如下方式獲取其中的關(guān)鍵數(shù)據(jù):
with self.client.post("/login", reqBody, catch_response=True) as response:
json_resp = response.json()
code = json_resp["code"]
data_len = len(json_resp["data"])
id = json_resp["data"][0]["id"]
自定義標(biāo)簽
從簡介的Summary report圖中可以看到,Locust的結(jié)果展示中,請求的默認(rèn)名稱是url的path部分,而為了報(bào)告更直觀,或者當(dāng)同一個(gè)業(yè)務(wù)有動(dòng)態(tài)的path(如/user/[userid]),需要聚合時(shí),可以通過name參數(shù)來自定義標(biāo)簽實(shí)現(xiàn),例:
with self.client.get("/account/{accountID}", catch_response=True, name = "getAccount") as resp:
分布式運(yùn)行
Locust的分布式運(yùn)行,master和slave節(jié)點(diǎn)都需要有場景腳本,分別以如下命令啟動(dòng):
- master:
locust -f locustfile.py --master --web-host=x.x.x.x - slave:
locust -f locustfile.py --slave --master-host=x.x.x.x
master節(jié)點(diǎn)將運(yùn)行Locust的Web UI服務(wù),不會(huì)承擔(dān)任何施壓任務(wù)(不會(huì)模擬虛擬用戶)。
如前面的簡介,Locust模擬并發(fā)用戶是使用協(xié)程,也因此對于多核CPU的服務(wù)器,為良好的利用多核能力,建議一臺(tái)slave服務(wù)器運(yùn)行與CPU核數(shù)相當(dāng)?shù)膕lave。
總結(jié)
Locust作為一個(gè)年輕的、輕量級(jí)的性能測試工具,網(wǎng)絡(luò)上相關(guān)的應(yīng)用文獻(xiàn)特別是中文文獻(xiàn)相對較少,而且多數(shù)是Quick start式的指引,對于一些實(shí)施的細(xì)節(jié)信息還比較欠缺,本文根據(jù)作者的實(shí)際應(yīng)用經(jīng)驗(yàn),列舉了部分使用細(xì)節(jié),希望能為需要的朋友提供一點(diǎn)有用的信息。