https://www.cnblogs.com/yoyoketang/p/9638151.html
https://www.cnblogs.com/yoyoketang/p/9642242.html
https://www.cnblogs.com/yoyoketang/p/9648100.html
https://www.cnblogs.com/yoyoketang/p/9672096.html
https://www.cnblogs.com/yoyoketang/p/9683897.html
Locust性能測試1-環境准備與基本使用
前言
提到性能測試,大部分小伙伴想到的就是LR和jmeter這種工具,小編一直不太喜歡寫這種工具類的東西,我的原則是能用代碼解決的問題,盡量不去用工具。
python里面也有一個性能測試框架Locust,本篇簡單的介紹Locust的基本使用,希望越來越多的小伙伴能一起愛上它!
環境准備:
python3.6
windows電腦
locust
Locust簡介
Locust是一款易於使用的分布式用戶負載測試工具。它用於對網站(或其他系統)進行負載測試,並確定系統可以處理多少並發用戶。
這個想法是,在測試期間,一群蝗蟲(Locust)會攻擊你的網站。您定義了每個蝗蟲Locust(或測試用戶)的行為,並且實時地從Web UI監視群集過程。這將有助於您在讓真正的用戶進入之前進行測試並識別代碼中的瓶頸。
Locust完全基於事件,因此可以在一台計算機上支持數千個並發用戶。與許多其他基於事件的應用程序相比,它不使用回調。相反,它通過協程(gevent)機制使用輕量級過程。每個蝗蟲蜂擁到你的網站實際上是在自己的進程內運行(或者是greenlet,這是正確的)。這允許您在Python中編寫非常富有表現力的場景,而不會使代碼復雜化。
** gevent是第三方庫,通過greenlet實現協程。greenlet是python的並行處理的一個庫。 python 有一個非常有名的庫叫做 stackless ,用來做並發處理, 主要是弄了個叫做tasklet的微線程的東西, 而greenlet 跟stackless的最大區別是greenlet需要你自己來處理線程切換, 就是說,你需要自己指定現在執行哪個greenlet再執行哪個greenlet。**
環境安裝
Locust支持Python 2.7, 3.4, 3.5, and 3.6的版本,小編的環境是python3.6直接用pip安裝就行
快速開始
locust里面請求是基於requests的,每個方法請求和requests差不多,請求參數、方法、響應對象和requests一樣的使用,之前學過requests庫的,這里就非常簡單了
- requests.get 對應client.get
- requests.post 對應client.post
# 保存為demo.py # coding:utf-8 from locust import HttpLocust,TaskSet,task class BlogDemo(TaskSet): '''用戶行為:打開我的博客首頁demo''' @task(1) def open_blog(self): # 定義requests的請求頭 header = {"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"} r = self.client.get("/yoyoketang", headers=header, verify=False) print(r.status_code) assert r.status_code == 200 class websitUser(HttpLocust): task_set = BlogDemo min_wait = 3000 # 單位毫秒 max_wait = 6000 # 單位毫秒 if __name__ == "__main__": import os os.system("locust -f demo.py --host=https://www.cnblogs.com")
代碼注解:
新建一個類BlogDemo(TaskSet),繼承TaskSet,該類下面寫一些准備請求的行為(訪問的接口)
里面的self.client調用get和post方法,跟requests是一樣的
@task裝飾該方法表示為用戶行為。括號里面參數表示該行為挑選執行的權重,數值越大,執行頻率越高,不設置默認是1
WebsiteUser()類用於設置性能測試。
task_set :指向一個定義了的用戶行為類。
min_wait :用戶執行任務之間等待時間的下界,單位:毫秒。
max_wait :用戶執行任務之間等待時間的上界,單位:毫秒。
啟動locust
啟動locust可以直接在pycharm里面執行上面的代碼,運行后編輯器出現兩行
[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執行
$ locust -f demo.py --host=https://www.cnblogs.com
- -f 參數是指定運行的腳本
- --host是指定運行項目的host地址,這里用的https://www.cnblogs.com,代碼里面get訪問的是"/yoyoketang",拼接起來就是完整地址了
8089是該服務啟動的端口號。由於是在本機上搭建的locust,所以可以直接在瀏覽器輸入http://localhost:8089/打開,
如果是在其它機器上搭建的locust服務,那就通過http://其它機器IP:8089/打開
Number of users to simulate 設置虛擬用戶總數
Hatch rate (users spawned/second) 每秒啟動虛擬用戶數
點擊Start swarming 開始運行性能測試
效果展示
設置虛擬用戶數30,每秒啟動5個用戶,點擊Start swarming 開始運行
Type:請求類型;
Name:請求路徑;
requests:當前請求的數量;
fails:當前請求失敗的數量;
Median:中間值,單位毫秒,一般服務器響應時間低於該值,而另一半高於該值;
Average:所有請求的平均響應時間,毫秒;
Min:請求的最小的服務器響應時間,毫秒;
Max:請求的最大服務器響應時間,毫秒;
Content Size:單個請求的大小,單位字節;
reqs/sec:每秒鍾請求的個數。
點stop可以停止測試
三個圖標分別是
吞吐量/每秒響應事務數(rps)實時統計
平均響應時間/平均事務數實時統計
虛擬用戶數運行
===============================================================================================================
===============================================================================================================
===============================================================================================================
Locust性能測試2-先登錄場景案例
前言
有很多網站不登錄的話,是無法訪問到里面的頁面的,這就需要先登錄了
實現場景:先登錄(只登錄一次),然后訪問頁面->我的地盤頁->產品頁->項目頁
官方案例
下面是一個簡單的locustfile.py的簡單示例:
from locust import HttpLocust, TaskSet def login(l): l.client.post("/login", {"username":"ellen_key", "password":"education"}) def logout(l): l.client.post("/logout", {"username":"ellen_key", "password":"education"}) def index(l): l.client.get("/") def profile(l): l.client.get("/profile") class UserBehavior(TaskSet): tasks = {index: 2, profile: 1} def on_start(self): login(self) def on_stop(self): logout(self) class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 5000 max_wait = 9000
這里我們定義了許多Locust任務,它們是帶有一個參數(Locust類實例)的普通Python callables 。這些任務收集在tasks屬性的TaskSet類下 。然后我們有一個代表用戶的 類,我們在其中定義模擬用戶在執行任務之間應該等待多長時間,以及哪個 類應該定義用戶的“行為”。 類可以繼承HttpLocust、TaskSet、TaskSet
HttpLocust類從繼承 Locust的類,並把它添加一個客戶端屬性,它是的一個實例 HttpSession,可用於使HTTP請求。
另一種我們可以聲明任務的方法,通常是更方便,就是使用 @task裝飾器。以下代碼與上述代碼相同:
from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): def on_start(self): """ on_start is called when a Locust start before any task is scheduled """ self.login() def on_stop(self): """ on_stop is called when the TaskSet is stopping """ self.logout() def login(self): self.client.post("/login", {"username":"ellen_key", "password":"education"}) def logout(self): self.client.post("/logout", {"username":"ellen_key", "password":"education"}) @task(2) def index(self): self.client.get("/") @task(1) def profile(self): self.client.get("/profile") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 5000 max_wait = 9000
在Locust類(以及HttpLocust 因為它是一個子類),也可以讓一個在指定最小和最大等待時間毫秒,每個模擬用戶之間的任務執行(min_wait和MAX_WAIT)以及其他用戶的行為。默認情況下,時間是在min_wait和max_wait之間統一隨機選擇的,但是可以通過將wait_function設置為任意函數來使用任何用戶定義的時間分布。例如,對於指數分布的等待時間平均為1秒:
import random class WebsiteUser(HttpLocust): task_set = UserBehaviour wait_function = lambda self: random.expovariate(1)*1000
項目實例
上面的官方案例只是一些偽代碼,不能在真實的環境中跑起來,接下來把上面的理論執行用到真實的項目環境中
http協議是無狀態的,所以登錄請求和登錄后的請求它是獨立的,但是登錄后的請求需要依賴先登錄拿到cookies,才能保持登錄狀態,這個在之前python接口自動化里面可以用session來解決
s = requests.session()
HttpLocust類從繼承 Locust的類,並把它添加一個客戶端屬性,它是的一個實例 HttpSession,可用於使HTTP請求,這就相當於它自動使用了session機制,類似於client = requests.session()
所以后面的請求,直接拿client.get()、client.post()請求就可以了
# 保存為locustfile.py # coding=utf-8 from locust import HttpLocust, TaskSet, task ''' 實現場景:先登錄(只登錄一次),然后訪問->我的地盤頁->產品頁->項目頁 訪問我的地盤頁面權重為2,產品頁和項目頁權重各為1 ***作者:上海-悠悠 QQ群:588402570** ''' class UserBehavior(TaskSet): '''蝗蟲行為類''' def _login(self): '''登錄方法''' # host = 'http://192.168.x.xx:80' # 禪道的服務器地 loginUrl ="/zentao/user-login.html/" h = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0", "Content-Type": "application/x-www-form-urlencoded", } body = {"account": "yoyo", # 你自己的賬號 "password": "******", # 你自己的密碼 "keepLogin[]": "on", "referer": "/zentao/my/" } r = self.client.post(loginUrl, data=body, headers=h) print(r.text) assert "parent.location='/zentao/index.html'" in r.text def on_start(self): '''任務開始准備工作:只登錄一次''' self._login() # 任務1-我的地盤 @task(2) def zentao_my(self): print("---訪問頁面-我的地盤---") r = self.client.get("/zentao/my/") assert "我的地盤" in r.text # 任務2-產品頁 @task(1) def zentao_product(self): print("---訪問頁面-產品頁---") r = self.client.get("/zentao/product-browse.html/") assert "需求列表" in r.text # 任務3-項目 @task(1) def zentao_prject(self): print("---訪問頁面-項目---") r = self.client.get("/zentao/project/") assert "項目首頁" in r.text class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 1000 max_wait = 1000 if __name__ == "__main__": import os os.system("locust -f locustfile.py --host=http://192.168.x.xx:80")
===============================================================================================================
===============================================================================================================
===============================================================================================================
Locust性能測試3-no-web模式和csv報告保存
前言
前面是在web頁面操作,需要手動的點start啟動,結束的時候也需要手工去點stop,沒法自定義運行時間,這就不太方便。
locust提供了命令行運行的方法,不啟動web頁面也能運行,這就是no-web模式啟動
無web-UI模式
在沒有Web UI的情況下運行locust - 可以打開cmd 通過使用--no-web參數,
- -c指定要生成的Locust用戶數
- -r每秒啟動虛擬用戶數
先cd到腳本當前目錄,然后執行指令
locust -f locustfile.py --host=http://192.168.x.xx:80 --no-web -c 1 -r 1
# 設置運行時間
如果要指定測試的運行時間,可以使用--run-time
locust -f locustfile.py --host=http://192.168.x.xx:80 --no-web -c 1 -r 1 --run-time 10
或使用-t參數
locust -f locustfile.py --host=http://192.168.x.xx:80 --no-web -c 1 -r 1 -t 10
運行時間單位,如果不寫單位默認是s,也可以指定小時h,分鍾m,可以參考以下時間格式
- 10s 10秒(不寫單位默認s)
- 5m 表示5分鍾
- 1h 1小時
- 1m30s 1分30秒
導出csv格式報告
您可能希望通過CSV文件保存的Locus結果。在這種情況下,有兩種方法可以做到這一點。
首先,使用Web UI運行Locust時,您可以在“下載數據”選項卡下點擊下載CSV文件。
- Download request statistics CSV
- Download response time distribution CSV
- Download exceptions CSV
===============================================================================================================
===============================================================================================================
===============================================================================================================
Locust性能測試4-參數關聯
前言
前面【Locust性能測試2-先登錄場景案例】講了登錄的案例,這種是直接傳賬號和密碼就能登錄了,有些登錄的網站會復雜一點,
需要先從頁面上動態獲取參數,作為登錄接口的請求參數,如【學信網:https://account.chsi.com.cn/passport/login】的登錄接口請求參數
請求參數
需要先發個get請求,從返回的html頁面中解析出需要的數據
lt : LT-277623-5ldGTLqQhP4foKihHUlgfKPeGGyWVI
execution: e1s1
備注:
lt 參數是每次打開瀏覽器,訪問登錄首頁時服務端會返回一個新的數據
execution 參數是表示網站刷新次數,可以刷新下再登錄,就變成 e2s1了
<input class="btn_login" name="submit" accesskey="l" value="登錄" tabindex="4" type="submit" title="登錄" /> <div class="account-oprate clearfix"> <a class="find-yhm" href="https://account.chsi.com.cn/account/password!rtvlgname">找回用戶名</a> <a class="find-mm" href="https://account.chsi.com.cn/account/password!retrive">找回密碼</a> <a href="https://account.chsi.com.cn/account/preregister.action?from=account-login" class="regist-btn">注冊</a> </div> <input type="hidden" name="lt" value="LT-279621-fnisPBt0FVGNFrfWzJJqhTEyw6VkfH" /> <input type="hidden" name="execution" value="e2s1" /> <input type="hidden" name="_eventId" value="submit" />
locustfile3.py代碼
前面用篇專門講了requests實現接口的參數關聯案例,這里直接轉化成locust腳本就行了
# coding:utf-8 from locust import HttpLocust, TaskSet, task from lxml import etree class 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、execution dom = 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參數獲取失敗!") 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): # 測試數據 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性能測試5-參數化批量注冊
前言
實現場景:所有並發虛擬用戶共享同一份測試數據,並且保證虛擬用戶使用的數據不重復。
例如,模擬10用戶並發注冊賬號,總共有100個手機號,要求注冊賬號不重復,注冊完畢后結束測試
准備數據

虛擬用戶數,可以在啟動的時候設置,這里先裝備好注冊需要用到的手機號,可以用list生成
# 生成測試手機號 demo = 13812120000 teldatas = [str(demo+i) for i in range(100)] print(teldatas)
將測試數據加到隊列
import queue # 生成測試手機號 demo = 13812120000 teldatas = [str(demo+i) for i in range(100)] # print(teldatas) # 添加到隊列 telqueue = queue.Queue() for i in teldatas: telqueue.put_nowait(i)
注冊demo參考
以下是一個簡單的demo模型,具體的注冊接口替換過去就可以了
# 保存為 locustfile4.py # coding=utf-8 from locust import HttpLocust, TaskSet, task import queue class test_taskset(TaskSet): @task def register(self): try: tel = self.locust.telqueue.get() # 獲取隊列里的數據 print(tel) except queue.Empty: # 隊列取空后,直接退出 print("no data exist") exit(0) print("當前注冊手機號:%s" % tel) # body = { # "username": tel, # "psd": "123456", # } # self.client.post("/register", data=body) # POST方法發送請求 class test_run(HttpLocust): host = 'http://192.168.1.xxx:80' task_set = test_taskset # 生成測試手機號 teldatas = [str(13812120000+i) for i in range(100)] # 添加到隊列 telqueue = queue.Queue() for i in teldatas: telqueue.put_nowait(i) if __name__ == "__main__": import os os.system("locust -f locustfile4.py")