Locust性能測(cè)試1-環(huán)境準(zhǔn)備與基本使用
前言
提到性能測(cè)試餐弱,大部分小伙伴想到的就是LR和jmeter這種工具岩灭,小編一直不太喜歡寫這種工具類的東西瞄摊,我的原則是能用代碼解決的問題,盡量不去用工具裸删。
python里面也有一個(gè)性能測(cè)試框架Locust八拱,本篇簡(jiǎn)單的介紹Locust的基本使用,希望越來越多的小伙伴能一起愛上它烁落!
Locust簡(jiǎn)介
Locust是一款易于使用的分布式用戶負(fù)載測(cè)試工具乘粒。它用于對(duì)網(wǎng)站(或其他系統(tǒng))進(jìn)行負(fù)載測(cè)試,并確定系統(tǒng)可以處理多少并發(fā)用戶伤塌。
這個(gè)想法是灯萍,在測(cè)試期間,一群蝗蟲(Locust)會(huì)攻擊你的網(wǎng)站每聪。您定義了每個(gè)蝗蟲Locust(或測(cè)試用戶)的行為旦棉,并且實(shí)時(shí)地從Web UI監(jiān)視群集過程。這將有助于您在讓真正的用戶進(jìn)入之前進(jìn)行測(cè)試并識(shí)別代碼中的瓶頸药薯。
Locust完全基于事件绑洛,因此可以在一臺(tái)計(jì)算機(jī)上支持?jǐn)?shù)千個(gè)并發(fā)用戶。與許多其他基于事件的應(yīng)用程序相比童本,它不使用回調(diào)真屯。相反,它通過協(xié)程(gevent)機(jī)制使用輕量級(jí)過程穷娱。每個(gè)蝗蟲蜂擁到你的網(wǎng)站實(shí)際上是在自己的進(jìn)程內(nèi)運(yùn)行(或者是greenlet绑蔫,這是正確的)。這允許您在Py??thon中編寫非常富有表現(xiàn)力的場(chǎng)景泵额,而不會(huì)使代碼復(fù)雜化配深。
** gevent是第三方庫(kù),通過greenlet實(shí)現(xiàn)協(xié)程嫁盲。greenlet是python的并行處理的一個(gè)庫(kù)篓叶。 python 有一個(gè)非常有名的庫(kù)叫做 stackless ,用來做并發(fā)處理, 主要是弄了個(gè)叫做tasklet的微線程的東西缸托, 而greenlet 跟stackless的最大區(qū)別是greenlet需要你自己來處理線程切換左敌, 就是說,你需要自己指定現(xiàn)在執(zhí)行哪個(gè)greenlet再執(zhí)行哪個(gè)greenlet嗦董。**
環(huán)境安裝
Locust支持Python 2.7, 3.4, 3.5, and 3.6的版本母谎,小編的環(huán)境是python3.6直接用pip安裝就行
快速開始
locust里面請(qǐng)求是基于requests的,每個(gè)方法請(qǐng)求和requests差不多京革,請(qǐng)求參數(shù)奇唤、方法、響應(yīng)對(duì)象和requests一樣的使用匹摇,之前學(xué)過requests庫(kù)的咬扇,這里就非常簡(jiǎn)單了
requests.get 對(duì)應(yīng)client.get
requests.post 對(duì)應(yīng)client.post
代碼注解:
新建一個(gè)類BlogDemo(TaskSet),繼承TaskSet廊勃,該類下面寫一些準(zhǔn)備請(qǐng)求的行為(訪問的接口)
里面的self.client調(diào)用get和post方法懈贺,跟requests是一樣的
@task裝飾該方法表示為用戶行為。括號(hào)里面參數(shù)表示該行為挑選執(zhí)行的權(quán)重坡垫,數(shù)值越大梭灿,執(zhí)行頻率越高,不設(shè)置默認(rèn)是1
WebsiteUser()類用于設(shè)置性能測(cè)試冰悠。
task_set :指向一個(gè)定義了的用戶行為類堡妒。
min_wait :用戶執(zhí)行任務(wù)之間等待時(shí)間的下界,單位:毫秒溉卓。
max_wait :用戶執(zhí)行任務(wù)之間等待時(shí)間的上界皮迟,單位:毫秒。
啟動(dòng)locust
啟動(dòng)locust可以直接在pycharm里面執(zhí)行上面的代碼桑寨,運(yùn)行后編輯器出現(xiàn)兩行
[2018-09-12 23:23:57,500] DESKTOP-HJ487C8/INFO/locust.main: Starting web monitor at *:8089
[2018-09-12 23:23:57,500] DESKTOP-HJ487C8/INFO/locust.main: Starting Locust 0.9.0
也可以通過cmd執(zhí)行
$ locust -f demo.py --host=https://www.cnblogs.com
-f 參數(shù)是指定運(yùn)行的腳本
--host是指定運(yùn)行項(xiàng)目的host地址伏尼,這里用的https://www.cnblogs.com,代碼里面get訪問的是"/yoyoketang"尉尾,拼接起來就是完整地址了
8089是該服務(wù)啟動(dòng)的端口號(hào)爆阶。由于是在本機(jī)上搭建的locust,所以可以直接在瀏覽器輸入http://localhost:8089/打開沙咏,
如果是在其它機(jī)器上搭建的locust服務(wù)辨图,那就通過http://其它機(jī)器IP:8089/打開
Number of users to simulate 設(shè)置虛擬用戶總數(shù)
Hatch rate (users spawned/second) 每秒啟動(dòng)虛擬用戶數(shù)
點(diǎn)擊Start swarming 開始運(yùn)行性能測(cè)試
效果展示
設(shè)置虛擬用戶數(shù)30,每秒啟動(dòng)5個(gè)用戶芭碍,點(diǎn)擊Start swarming 開始運(yùn)行
Type:請(qǐng)求類型徒役;
Name:請(qǐng)求路徑孽尽;
requests:當(dāng)前請(qǐng)求的數(shù)量窖壕;
fails:當(dāng)前請(qǐng)求失敗的數(shù)量;
Median:中間值,單位毫秒瞻讽,一般服務(wù)器響應(yīng)時(shí)間低于該值鸳吸,而另一半高于該值;
Average:所有請(qǐng)求的平均響應(yīng)時(shí)間速勇,毫秒晌砾;
Min:請(qǐng)求的最小的服務(wù)器響應(yīng)時(shí)間,毫秒烦磁;
Max:請(qǐng)求的最大服務(wù)器響應(yīng)時(shí)間养匈,毫秒;
Content Size:?jiǎn)蝹€(gè)請(qǐng)求的大小都伪,單位字節(jié)呕乎;
reqs/sec:每秒鐘請(qǐng)求的個(gè)數(shù)。
點(diǎn)stop可以停止測(cè)試
三個(gè)圖標(biāo)分別是
吞吐量/每秒響應(yīng)事務(wù)數(shù)(rps)實(shí)時(shí)統(tǒng)計(jì)
平均響應(yīng)時(shí)間/平均事務(wù)數(shù)實(shí)時(shí)統(tǒng)計(jì)
虛擬用戶數(shù)運(yùn)行
Locust性能測(cè)試2-先登錄場(chǎng)景案例
前言
有很多網(wǎng)站不登錄的話陨晶,是無法訪問到里面的頁面的猬仁,這就需要先登錄了
實(shí)現(xiàn)場(chǎng)景:先登錄(只登錄一次),然后訪問頁面->我的地盤頁->產(chǎn)品頁->項(xiàng)目頁
官方案例
下面是一個(gè)簡(jiǎn)單的locustfile.py的簡(jiǎn)單示例:
這里我們定義了許多Locust任務(wù)先誉,它們是帶有一個(gè)參數(shù)(Locust類實(shí)例)的普通Python callables 湿刽。這些任務(wù)收集在tasks屬性的TaskSet類下 。然后我們有一個(gè)代表用戶的 類褐耳,我們?cè)谄渲卸x模擬用戶在執(zhí)行任務(wù)之間應(yīng)該等待多長(zhǎng)時(shí)間诈闺,以及哪個(gè) 類應(yīng)該定義用戶的“行為”。 類可以繼承HttpLocust漱病、TaskSet买雾、TaskSet
HttpLocust類從繼承 Locust的類,并把它添加一個(gè)客戶端屬性杨帽,它是的一個(gè)實(shí)例 HttpSession漓穿,可用于使HTTP請(qǐng)求。
另一種我們可以聲明任務(wù)的方法注盈,通常是更方便晃危,就是使用 @task裝飾器。以下代碼與上述代碼相同:
在Locust類(以及HttpLocust 因?yàn)樗且粋€(gè)子類)老客,也可以讓一個(gè)在指定最小和最大等待時(shí)間毫秒僚饭,每個(gè)模擬用戶之間的任務(wù)執(zhí)行(min_wait和MAX_WAIT)以及其他用戶的行為。默認(rèn)情況下胧砰,時(shí)間是在min_wait和max_wait之間統(tǒng)一隨機(jī)選擇的鳍鸵,但是可以通過將wait_function設(shè)置為任意函數(shù)來使用任何用戶定義的時(shí)間分布。例如尉间,對(duì)于指數(shù)分布的等待時(shí)間平均為1秒:
????import randomclass WebsiteUser(HttpLocust):
? ????? task_set = UserBehaviour
? ????? wait_function =lambdaself: random.expovariate(1)*1000
項(xiàng)目實(shí)例
上面的官方案例只是一些偽代碼偿乖,不能在真實(shí)的環(huán)境中跑起來击罪,接下來把上面的理論執(zhí)行用到真實(shí)的項(xiàng)目環(huán)境中
http協(xié)議是無狀態(tài)的,所以登錄請(qǐng)求和登錄后的請(qǐng)求它是獨(dú)立的贪薪,但是登錄后的請(qǐng)求需要依賴先登錄拿到cookies,才能保持登錄狀態(tài)媳禁,這個(gè)在之前python接口自動(dòng)化里面可以用session來解決
s = requests.session()
HttpLocust類從繼承 Locust的類,并把它添加一個(gè)客戶端屬性画切,它是的一個(gè)實(shí)例 HttpSession竣稽,可用于使HTTP請(qǐng)求,這就相當(dāng)于它自動(dòng)使用了session機(jī)制,類似于client = requests.session()所以后面的請(qǐng)求霍弹,直接拿client.get()毫别、client.post()請(qǐng)求就可以了
Locust性能測(cè)試3-no-web模式和csv報(bào)告保存
前言
前面是在web頁面操作,需要手動(dòng)的點(diǎn)start啟動(dòng)典格,結(jié)束的時(shí)候也需要手工去點(diǎn)stop,沒法自定義運(yùn)行時(shí)間拧烦,這就不太方便。
locust提供了命令行運(yùn)行的方法钝计,不啟動(dòng)web頁面也能運(yùn)行恋博,這就是no-web模式啟動(dòng)
無web-UI模式
在沒有Web UI的情況下運(yùn)行l(wèi)ocust - 可以打開cmd 通過使用--no-web參數(shù),
-c指定要生成的Locust用戶數(shù)
-r每秒啟動(dòng)虛擬用戶數(shù)
先cd到腳本當(dāng)前目錄私恬,然后執(zhí)行指令
locust -f locustfile.py --host=http://192.168.x.xx:80 --no-web -c 1 -r 1
# 設(shè)置運(yùn)行時(shí)間
如果要指定測(cè)試的運(yùn)行時(shí)間债沮,可以使用--run-time
locust -f locustfile.py --host=http://192.168.x.xx:80 --no-web -c 1 -r 1 --run-time 10
# 或使用-t參數(shù)
locust -f locustfile.py --host=http://192.168.x.xx:80 --no-web -c 1 -r 1 -t 10
運(yùn)行時(shí)間單位,如果不寫單位默認(rèn)是s,也可以指定小時(shí)h,分鐘m本鸣,可以參考以下時(shí)間格式
# 10s 10秒(不寫單位默認(rèn)s)
#?5m 表示5分鐘
#?1h 1小時(shí)
#?1m30s 1分30秒
導(dǎo)出csv格式報(bào)告
您可能希望通過CSV文件保存的Locus結(jié)果疫衩。在這種情況下,有兩種方法可以做到這一點(diǎn)荣德。
首先闷煤,使用Web UI運(yùn)行Locust時(shí),您可以在“下載數(shù)據(jù)”選項(xiàng)卡下點(diǎn)擊下載CSV文件涮瞻。
Download request statistics CSV
Download response time distribution CSV
Download exceptions CSV
Locust性能測(cè)試4-參數(shù)關(guān)聯(lián)
前言
前面【Locust性能測(cè)試2-先登錄場(chǎng)景案例】講了登錄的案例鲤拿,這種是直接傳賬號(hào)和密碼就能登錄了,有些登錄的網(wǎng)站會(huì)復(fù)雜一點(diǎn)署咽,
需要先從頁面上動(dòng)態(tài)獲取參數(shù)近顷,作為登錄接口的請(qǐng)求參數(shù),如【學(xué)信網(wǎng):https://account.chsi.com.cn/passport/login】的登錄接口請(qǐng)求參數(shù)
請(qǐng)求參數(shù)
需要先發(fā)個(gè)get請(qǐng)求宁否,從返回的html頁面中解析出需要的數(shù)據(jù)
lt : LT-277623-5ldGTLqQhP4foKihHUlgfKPeGGyWVI
execution: e1s1
備注:
lt 參數(shù)是每次打開瀏覽器窒升,訪問登錄首頁時(shí)服務(wù)端會(huì)返回一個(gè)新的數(shù)據(jù)
execution 參數(shù)是表示網(wǎng)站刷新次數(shù),可以刷新下再登錄慕匠,就變成 e2s1了
locustfile3.py代碼
前面用篇專門講了requests實(shí)現(xiàn)接口的參數(shù)關(guān)聯(lián)案例饱须,這里直接轉(zhuǎn)化成locust腳本就行了
===============================================================================================================
# coding:utf-8fromlocustimport HttpLocust, TaskSet, taskfromlxmlimport etreeclass LoginDemo(TaskSet):
? ? '''用戶行為描述'''def get_it_execution(self):
? ? ? ? result = {}
? ? ? ? h1 = {
? ? ? ? ? ? "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
? ? ? ? }
? ? ? ? self.client.headers.update(h1)
? ? ? ? r = self.client.get("/passport/login", verify=False)
? ? ? ? # 從返回html頁面,解析出lt台谊、executiondom = etree.HTML(r.content.decode("utf-8"))
? ? ? ? try:? result["lt"] = dom.xpath('//input[@name="lt"]')[0].get("value")
? ? ? ? ? ? result["execution"] = dom.xpath('//input[@name="execution"]')[0].get("value")
? ? ? ? ? ? print(result)
? ? ? ? except:
? ? ? ? ? ? print("lt、execution參數(shù)獲取失敗博秫!")
? ? ? ? return result
? ? def login(self, user, psw):
? ? ? ? result = self.get_it_execution()
? ? ? ? loginurl ="/passport/login"? ? ? ? h2 = {
? ? ? ? ? ? "Referer": loginurl,
? ? ? ? ? ? "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
? ? ? ? ? ? "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
? ? ? ? ? ? "Origin":"https://account.chsi.com.cn",
? ? ? ? ? ? "Content-Length":"119",
? ? ? ? ? ? "Cache-Control":"max-age=0",
? ? ? ? ? ? "Upgrade-Insecure-Requests":"1",
? ? ? ? ? ? "Content-Type":"application/x-www-form-urlencoded"? ? ? ? ? ? }
? ? ? ? body = {
? ? ? ? ? ? "username": user,
? ? ? ? ? ? "password": psw,
? ? ? ? ? ? "rememberMe":"true",
? ? ? ? ? ? "lt": result["lt"],
? ? ? ? ? ? "execution": result["execution"],
? ? ? ? ? ? "_eventId":"submit"? ? ? ? }
? ? ? ? self.client.headers.update(h2)
? ? ? ? print(self.client.headers)
? ? ? ? r1 = self.client.post(loginurl, data=body, verify=False)
? ? ? ? # print(r1.text)? ? @task(1)
? ? def test_login(self):
? ? ? ? # 測(cè)試數(shù)據(jù)user ="13888888888"? ? ? ? psw ="111111"? ? ? ? self.login(user, psw)class websitUser(HttpLocust):
? ? task_set = LoginDemo
? ? host ="https://account.chsi.com.cn"? ? min_wait = 3000# 單位毫秒max_wait = 6000# 單位毫秒if__name__=="__main__":
? ? import os
? ? os.system("locust -f locustfile3.py")
?===============================================================================================================
Locust性能測(cè)試5-參數(shù)化批量注冊(cè)
前言
實(shí)現(xiàn)場(chǎng)景:所有并發(fā)虛擬用戶共享同一份測(cè)試數(shù)據(jù),并且保證虛擬用戶使用的數(shù)據(jù)不重復(fù)。
例如狠角,模擬10用戶并發(fā)注冊(cè)賬號(hào)号杠,總共有100個(gè)手機(jī)號(hào),要求注冊(cè)賬號(hào)不重復(fù)丰歌,注冊(cè)完畢后結(jié)束測(cè)試
準(zhǔn)備數(shù)據(jù)
虛擬用戶數(shù)姨蟋,可以在啟動(dòng)的時(shí)候設(shè)置,這里先裝備好注冊(cè)需要用到的手機(jī)號(hào)立帖,可以用list生成
# 生成測(cè)試手機(jī)號(hào)demo = 13812120000teldatas = [str(demo+i)foriinrange(100)]print(teldatas)
將測(cè)試數(shù)據(jù)加到隊(duì)列
import queue# 生成測(cè)試手機(jī)號(hào)demo = 13812120000teldatas = [str(demo+i)foriinrange(100)]# print(teldatas)# 添加到隊(duì)列telqueue = queue.Queue()foriin teldatas:
? ? telqueue.put_nowait(i)
注冊(cè)demo參考
以下是一個(gè)簡(jiǎn)單的demo模型眼溶,具體的注冊(cè)接口替換過去就可以了
# 保存為 locustfile4.py#? coding=utf-8fromlocustimport HttpLocust, TaskSet, taskimport queueclass test_taskset(TaskSet):
? ? @task
? ? def register(self):
? ? ? ? try:
? ? ? ? ? ? tel = self.locust.telqueue.get()# 獲取隊(duì)列里的數(shù)據(jù)print(tel)
? ? ? ? exceptqueue.Empty:# 隊(duì)列取空后,直接退出print("no data exist")
? ? ? ? ? ? exit(0)
? ? ? ? print("當(dāng)前注冊(cè)手機(jī)號(hào):%s"% tel)
? ? ? ? # body = {#? ? "username": tel,#? ? "psd": "123456",#? ? }# self.client.post("/register", data=body)? # POST方法發(fā)送請(qǐng)求class test_run(HttpLocust):
? ? host ='http://192.168.1.xxx:80'? ? task_set = test_taskset
? ? # 生成測(cè)試手機(jī)號(hào)teldatas = [str(13812120000+i)foriinrange(100)]
? ? # 添加到隊(duì)列telqueue = queue.Queue()
? ? foriin teldatas:
? ? ? ? telqueue.put_nowait(i)if__name__=="__main__":
? ? import os
? ? os.system("locust -f locustfile4.py")