概述
- Locust寓意蝗蟲媚送,蝗蟲過境噩咪,寸草不生;而Locust工具生成并發(fā)請求就和一大群蝗蟲一般芋酌,向我們的被測系統發(fā)起攻擊增显,以此測試系統在高并發(fā)壓力下是否能正常運轉。
- Locust測試框架中脐帝,采用python進行開發(fā)同云,對常見的http(s)協議的系統,Locust采用request庫作為客戶端堵腹,在發(fā)請求時和request庫使用方法一樣炸站。
- 在模擬并發(fā)時,Locust采用協程秸滴、非阻塞IO來實現網絡層的并發(fā)請求武契,因此單臺壓力機也能產生數千并發(fā)請求,再加上對分布式運行的支持荡含,Locust能在使用較少壓力機的前提下支持極高的并發(fā)數測試咒唆。
Locust安裝和使用
安裝
- 在終端中運行: pip3 install locust
或者通過GitHub上克隆項目安裝(Python3推薦):https://github.com/locustio/locust - 安裝pyzmq
如果打算運行Locust分布在多個進程/及其,建議安裝pyzmq
通過pip命令安裝:pip install pyzmq - 驗證是否安裝成功: 在終端中輸入locust --help 顯示幫助信息既表示安裝成功
- 登錄locust官網:https://www.locust.io/
- 大并發(fā)量測試時释液,建議在linux系統下進行
編寫性能測試腳本
# coding=utf-8
from locust import TaskSet, HttpUser, task, between
class UserBehavior(TaskSet):
def on_start(self):
print("開始的時候執(zhí)行")
def on_stop(self):
print("結束的時候執(zhí)行")
@task()
def baidu(self):
self.client.get("/")
class WebsiteUser(HttpUser):
tasks = [UserBehavior]
wait_time = between(1, 2)
host = "https://www.baidu.com"
創(chuàng)建UserBehavior()類繼承TaskSet類全释,為用戶行為。
創(chuàng)建baidu() 方法表示一個行為误债,訪問百度首頁浸船。用@task() 裝飾該方法為一個任務。1表示一個Locust實例被挑選執(zhí)行的權重寝蹈,數值越大李命,執(zhí)行頻率越高。在當前UserBehavior()行為下只有一個baidu()任務箫老,所以封字,這里的權重設置為幾,并無影響。
WebsiteUser()類用于設置性能測試阔籽。
task_set :指向一個定義了的用戶行為類流妻。
min_wait :用戶執(zhí)行任務之間等待時間的下界,單位:毫秒笆制。
max_wait :用戶執(zhí)行任務之間等待時間的上界绅这,單位:毫秒。
運行性能測試
- 在終端中切換到性能測試腳本所在的目錄下
- 啟動性能測試:locust -f load_test.py --host=https://www.baidu.com
load_test.py 為測試腳本在辆,https://www.baidu.com 為測試的網站证薇。
輸出為:
[2022-02-24 10:54:13,528] shanshandeMacBook-Pro.local/INFO/locust.main: Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces)
[2022-02-24 10:54:13,551] shanshandeMacBook-Pro.local/INFO/locust.main: Starting Locust 2.8.2 -
打開瀏覽器,訪問http://127.0.0.1:8089
- Number of users to simulate 設置模擬用戶數
- Hatch rate (users spawned/second) 每秒產生(啟動)的用戶數匆篓。
-
點擊Start swarming 開始運行性能測試棕叫。
性能測試參數
- Type: 請求的類型,例如GET/POST奕删。
- Name:請求的路徑俺泣。這里為百度首頁,即:https://www.baidu.com/
- request:當前請求的數量完残。
- fails:當前請求失敗的數量伏钠。
- Median:中間值,單位毫秒谨设,一半的服務器響應時間低于該值熟掂,而另一半高于該值。
- Average:平均值扎拣,單位毫秒赴肚,所有請求的平均響應時間。
- Min:請求的最小服務器響應時間二蓝,單位毫秒誉券。
- Max:請求的最大服務器響應時間,單位毫秒刊愚。
- Content Size:單個請求的大小踊跟,單位字節(jié)。
- reqs/sec:是每秒鐘請求的個數鸥诽。
Locust no-web模式
熟悉 Apache ab 工具的同學都知道商玫,它是沒有界面的,通過命令行執(zhí)行牡借。 Locust 同樣也提供的命令行運行拳昌,好處就是更節(jié)省客戶端資源。
locust -f zkxl_verify_locust.py --host=https://www.baidu.com --headless -u 10 -r 2 -t 1m
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/locust/util/deprecation.py:14: DeprecationWarning: Usage of User.task_set is deprecated since version 1.0. Set the tasks attribute instead (tasks = [UserBehavior])
warnings.warn(
locust: error: unrecognized arguments: --no-web -c
啟動參數:
--no-web 表示不使用Web界面運行測試钠龙。
-c 設置虛擬用戶數炬藤。
-r 設置每秒啟動虛擬用戶數扁远。
-t 設置設置運行時間。
出現的報錯及解決辦法:
使用Locust進行性能測試刻像,Locust no-web模式執(zhí)行命令locust -f zkxl_verify_locust.py --host=https://www.baidu.com --no-web -c 10 -r 2 -t 1m
提示locust: error: unrecognized arguments: --no-web -c
參考locust官方文檔https://docs.locust.io/en/latest/running-locust-without-web-ui.html?highlight=no-web
將命令參數--no-web 更改為 --headless,將命令中指定用戶并發(fā)數的參數 -c 改為 -u并闲,即更改命令為:locust -f zkxl_verify_locust.py --host=https://www.baidu.com --headless -u 10 -r 2 -t 1m 即可.
Locust--CSV存儲測試數據
locust的測試數據可以保存到CSV文件中细睡,有兩種方法可以進行此操作:
首先,通過Web UI運行Locust時帝火,可以在“Download Data”選項卡下得到CSV文件溜徙。
其次,可以使用標簽運行Locust犀填,該標簽將定期保存兩個CSV文件蠢壹。如果計劃使用--no-web標簽以自動化方式運行Locust
$ locust -f examples/basic.py --csv=example --no-web -t10m
文件將被命名為example_response_times.csv 和 example_stats.csv (使用--csv=example)并記錄Locust構建的信息。
如果你想要更快(慢)的寫入速度九巡,也可以自動以寫入頻率:
import locust.stats
# 默認為2秒
locust.stats.CSV_STATS_INTERVAL_SEC = 5
此數據將寫入兩個文件图贸,并將_response_times.csv和_stats.csv添加到你提供的名稱中:
$ cat example_response_times.csv
"Name","# requests","50%","66%","75%","80%","90%","95%","98%","99%","99.9%","99.99%","100%"
"GET /",31,4,4,4,4,4,4,4,4,4,4,4
"/does_not_exist",0,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A"
"GET /stats/requests",38,3,4,4,4,4,5,5,5,5,5,5
"None Total",69,3,4,4,4,4,4,5,5,5,5,5
和
$ cat example_stats.csv
"Type","Name","# requests","# failures","Median response time","Average response time","Min response time","Max response time","Average Content Size","Requests/s"
"GET","/",51,0,4,3,2,6,12274,0.89
"GET","/does_not_exist",0,56,0,0,0,0,0,0.00
"GET","/stats/requests",58,0,3,3,2,5,1214,1.01
"None","Total",109,56,3,3,2,6,6389,1.89
Locust 常用參數說明
打開命令提示符(或Linux終端),輸入 locust --help 冕广。
參數 | 說明 |
---|---|
-h, --help | 查看幫助 |
-H HOST, --host=HOST | 指定被測試的主機疏日,采用以格式:http://10.21.32.33 |
--web-host=WEB_HOST | 指定運行 Locust Web 頁面的主機,默認為空 ''撒汉。 |
-P PORT, --port=PORT, --web-port=PORT | 指定 --web-host 的端口沟优,默認是8089 |
-f LOCUSTFILE, --locustfile=LOCUSTFILE | 指定運行 Locust 性能測試文件,默認為: locustfile.py |
--csv=CSVFILEBASE, --csv-base-name=CSVFILEBASE | 以CSV格式存儲當前請求測試數據睬辐。 |
--master | Locust 分布式模式使用挠阁,當前節(jié)點為 master 節(jié)點。 |
--slave | Locust 分布式模式使用溯饵,當前節(jié)點為 slave 節(jié)點侵俗。 |
--master-host=MASTER_HOST | 分布式模式運行,設置 master 節(jié)點的主機或 IP 地址丰刊,只在與 --slave 節(jié)點一起運行時使用坡慌,默認為:127.0.0.1. |
--master-port=MASTER_PORT | 分布式模式運行, 設置 master 節(jié)點的端口號藻三,只在與 --slave 節(jié)點一起運行時使用洪橘,默認為:5557。注意棵帽,slave 節(jié)點也將連接到這個端口+1 上的 master 節(jié)點熄求。 |
--master-bind-host=MASTER_BIND_HOST | Interfaces (hostname, ip) that locust master should bind to. Only used when running with --master. Defaults to * (all available interfaces). |
--master-bind-port=MASTER_BIND_PORT | Port that locust master should bind to. Only used when running with --master. Defaults to 5557. Note that Locust will also use this port + 1, so by default the master node will bind to 5557 and 5558. |
--expect-slaves=EXPECT_SLAVES | How many slaves master should expect to connect before starting the test (only when --no-web used). |
--no-web | no-web 模式運行測試,需要 -c 和 -r 配合使用. |
-c NUM_CLIENTS, --clients=NUM_CLIENTS | 指定并發(fā)用戶數逗概,作用于 --no-web 模式弟晚。 |
-r HATCH_RATE, --hatch-rate=HATCH_RATE | 指定每秒啟動的用戶數,作用于 --no-web 模式。 |
-t RUN_TIME, --run-time=RUN_TIME | 設置運行時間, 例如: (300s, 20m, 3h, 1h30m). 作用于 --no-web 模式卿城。 |
-L LOGLEVEL, --loglevel=LOGLEVEL | 選擇 log 級別(DEBUG/INFO/WARNING/ERROR/CRITICAL). 默認是 INFO. |
--logfile=LOGFILE | 日志文件路徑枚钓。如果沒有設置,日志將去 stdout/stderr |
--print-stats | 在控制臺中打印數據 |
--only-summary | 只打印摘要統計 |
--no-reset-stats | Do not reset statistics once hatching has been completed瑟押。 |
-l, --list | 顯示測試類, 配置 -f 參數使用 |
--show-task-ratio | 打印 locust 測試類的任務執(zhí)行比例搀捷,配合 -f 參數使用. |
--show-task-ratio-json | 以 json 格式打印 locust 測試類的任務執(zhí)行比例,配合 -f 參數使用. |
-V, --version | 查看當前 Locust 工具的版本. |
Locust分布式運行
參考:官方文檔
分布式運行Locust
一旦單臺機器不夠模擬足夠多的用戶時多望,Locust支持運行在多臺機器中進行壓力測試嫩舟。
為了實現這個,你應該在 master 模式中使用--master
標記來啟用一個 Locust 實例怀偷。這個實例將會運行你啟動測試的 Locust 交互網站并查看實時統計數據家厌。master 節(jié)點的機器自身不會模擬任何用戶。相反椎工,你必須使用 --slave
標記啟動一臺到多臺 Locustslave 機器節(jié)點饭于,與標記 --master-host
一起使用(指出master機器的IP/hostname)。
常用的做法是在一臺獨立的機器中運行master维蒙,在slave機器中每個處理器內核運行一個slave實例镰绎。
注意:master 和每一臺 slave 機器,在運行分布式測試時都必須要有 locust 的測試文件木西。
示例
在 master 模式下啟動 Locust:
locust -f my_loucstfile.py --master
在每個 slave 中執(zhí)行(192.168.0.14 替換為你 msater 的IP):
locust -f my_locustfile.py --slave --master-host=192.168.0.14
參數說明
參數
--master
設置 Locust 為 master 模式畴栖。網頁交互會在這臺節(jié)點機器中運行。
--slave
設置 Locust 為 slave 模式八千。
--master-host=X.X.X.X
可選項吗讶,與 --slave
一起結合使用,用于設置 master 模式下的 master 機器的IP/hostname(默認設置為127.0.0.1)
--master-port=5557
可選項恋捆,與 --slave
一起結合使用照皆,用于設置 master 模式下的 master 機器中 Locust 的端口(默認為5557)。注意沸停,locust 將會使用這個指定的端口號膜毁,同時指定端口+1的號也會被占用。因此愤钾,5557 會被使用瘟滨,Locust將會使用 5557 和 5558。
--master-bind-host=X.X.X.X`
可選項能颁,與 --master
一起結合使用杂瘸。決定在 master 模式下將會綁定什么網絡接口。默認設置為*(所有可用的接口)伙菊。
--master-bind-port=5557
可選項败玉,與 --master
一起結合使用敌土。決定哪個網絡端口 master 模式將會監(jiān)聽。默認設置為 5557运翼。注意 Locust 會使用指定的端口號返干,同時指定端口+1的號也會被占用。因此血淌,5557 會被使用矩欠,Locust 將會使用 5557 和 5558。
--expect-slaves=X
在 no-web 模式下啟動 master 時使用六剥。master 將等待X連接節(jié)點在測試開始之前連接。
效果
如下圖峰伙,我啟動了一個 master 和兩個 slave疗疟,由兩個 slave 來向被測試系統發(fā)送請求。
類和方法
Locust類
client屬性:
- task_set: 指向一個TaskSet類瞳氓,TaskSet類定義了用戶的任務信息策彤,該屬性為必填;
- max_wait/min_wait: 每個用戶執(zhí)行兩個任務間隔時間的上下限(毫秒)匣摘,具體數值在上下限中隨機取值店诗,若不指定則默認間隔時間固定為1秒;
- host:被測系統的host音榜,當在終端中啟動locust時沒有指定--host參數時才會用到庞瘸;
- weight:同時運行多個Locust類時會用到,用于控制不同類型任務的執(zhí)行權重赠叼。如果希望讓某個 locust 類經常被執(zhí)行擦囊,可以在這些類上設置一個 weight 屬性。
TaskSet類
TaskSet類:實現了虛擬用戶所執(zhí)行任務的調度算法嘴办,包括規(guī)劃任務執(zhí)行順序(schedule_task)瞬场、挑選下一個任務(execute_next_task)、執(zhí)行任務(execute_task)涧郊、休眠等待(wait)贯被、中斷控制(interrupt)等等。
在此基礎上妆艘,我們就可以在TaskSet子類中采用非常簡潔的方式來描述虛擬用戶的業(yè)務測試場景彤灶,對虛擬用戶的所有行為(任務)進行組織和描述,并可以對不同任務的權重進行配置批旺。
在TaskSet子類中定義任務信息時枢希,可以采取兩種方式,@task裝飾器和tasks屬性朱沃。
from locust import TaskSet, task
class UserBehavior(TaskSet):
@task(1)
def test_job1(self):
self.client.get('/job1')
@task(2)
def test_job2(self):
self.client.get('/job2')
@task(1)中的數字表示任務的執(zhí)行頻率苞轿,數值越大表示執(zhí)行的頻率越高
TaskSet類詳細說明–tasks屬性
采用tasks屬性定義任務:
from locust import TaskSet
def test_job1(obj):
obj.client.get('/job1')
def test_job2(obj):
obj.client.get('/job2')
class UserBehavior(TaskSet):
tasks = {test_job1:1, test_job2:2}
tasks = {test_job1:1, test_job2:2}中茅诱,test_job1:1,test_job2:2表示事件執(zhí)行的頻率,即test_job2的執(zhí)行頻率是test_job1的兩倍
on_start函數
on_start函數是在Taskset子類中使用比較頻繁的函數搬卒。在正式執(zhí)行測試前執(zhí)行一次瑟俭,主要用于完成一些初始化的工作。
例如契邀,當測試某個搜索功能摆寄,而該搜索功能又要求必須為登錄態(tài)的時候,就可以先在on_start中進行登錄操作坯门,HttpLocust使用到了requests.Session微饥,因此后續(xù)所有任務執(zhí)行過程中就都具有登錄態(tài)了
控制任務的執(zhí)行順序
在TaskSequence類中,[email protected]_task()可以用來控制任務的執(zhí)行順序古戴;里面的數值越小執(zhí)行越靠前欠橘;
休眠等待(wait)
在Taskset類中,內置WAIT_TIME功能现恼,它用于確定模擬用戶在執(zhí)行任務之間將等待多長時間肃续。Locust提供了一些內置的函數,返回一些常用的wait_time方法叉袍。
1始锚、between(min,max)函數:用得比較多的函數
wait_time = between(3.0, 10.5):任務之間等待的時間是3到10.5秒之間的任意時間
還可以用任意函數來定義等待時間, 比如平均1秒的等待時間
wait_time = lambda self: random.expovariate(1)1000
2喳逛、constant(number)函數:
wait_time=constant(3):任務之間等待的時候是3秒鐘瞧捌,且等待的時候不能超過任務運行的總時間,也就是在執(zhí)行py文件時設置的時間
3润文、constant_pacing(number)*函數:
wait_time=constant_pacing(3):所以任務每隔3秒執(zhí)行察郁,但是當到達運行的總時間時,任務運行結束转唉;
TaskSets 嵌套
現實中有很多任務其實也是有嵌套結構的皮钠,比如用戶打開一個網頁首頁后,用戶可能會不喜歡這個網頁直接離開赠法,或者喜歡就留下來麦轰,留下來的話,可以選擇看書砖织、聽音樂款侵、或者離開;
中斷控制(interrupt)
在有Taskset嵌套的情況下侧纯,執(zhí)行子任務時, 通過 self.interrupt() 來終止子任務的執(zhí)行, 來回到父任務類中執(zhí)行, 否則子任務會一直執(zhí)行新锈;
在上一頁的案例中,在stay這個類中眶熬,對interrupt()方法的調用是非常重要的妹笆,這可以讓一個用戶跳出stay這個類有機會執(zhí)行l(wèi)eave這個任務块请,否則他一旦進入stay任務就會一直在看書或者聽音樂而難以自拔。
Locust中實現參數化
在進行接口多用戶并發(fā)測試時拳缠,數據的重復使用可能會造成腳本的失敗墩新,那么需要對用戶數據進行參數化來使腳本運行成功。
已登錄功能為例:
from locust import HttpLocust, TaskSet, task
from random import randint
# Web性能測試
class UserBehavior(TaskSet):
def on_start(self):
self.login()
# 隨機返回登錄用戶
def login_user():
users = {"user1":123456,"user2":123123,"user3":111222}
data = randint(1, 3)
username = "user"+str(data)
password = users[username]
return username, password
@task
def login(self):
username, password = login_user()
self.client.post("/login_action", {"username":username, "password":password})
class User(HttpLocust):
task_set = UserTask
min_wait = 1000
max_wait = 3000
host = "http://www.xxx.com"
創(chuàng)建 login_user() 方法窟坐,定義登錄字典 users , 通過randint 隨機獲取字典中的用戶數據海渊。
在 login() 登錄任務中,調用 login_user() 方法實現 隨機用戶的登錄哲鸳。
情景一
在此我們舉出百度搜索的例子臣疑,假設每個人搜索的內容是不相同的;那么我們可以假設把數據放到隊列中徙菠,然后從隊列中依次把數據取出來讯沈;
可以利用python中Queue隊列來進行處理;
Queue的種類:
Queue.Queue(maxsize=0):先進先出隊列
Queue.LifoQueue(maxsize=0):后進先出隊列
Queue.PriorityQueue(maxsize=0):構造一個優(yōu)先隊列
參數maxsize是個整數懒豹,指明了隊列中能存放的數據個數的上限芙盘。一旦達到上限驯用,插入會導致阻塞脸秽,直到隊列中的數據被消費掉。如果maxsize小于或者等于0蝴乔,隊列大小沒有限制
Queue的基本方法:
- Queue.Queue(maxsize=0) 如果maxsize小于1就表示隊列長度無限
- Queue.LifoQueue(maxsize=0) 如果maxsize小于1就表示隊列長度無限
- Queue.qsize() 返回隊列的大小
- Queue.empty() 如果隊列為空记餐,返回True,反之False
- Queue.full() 如果隊列滿了,返回True,反之False
- Queue.get([block[, timeout]]) 讀隊列薇正,timeout等待時間
- Queue.put(item, [block[, timeout]]) 寫隊列片酝,timeout等待時間
- Queue.queue.clear() 清空隊列
情景二
個別情況下測試數據可重復使用,因此我們可以把參數化數據定義為一個列表挖腰,在列表中取出數據雕沿;
Locust中實現關聯
在某些請求中,需要攜帶之前response中提取的參數猴仑,常見場景就是session_id审轮。Python中可用通過re正則匹配,對于返回的html頁面辽俗,可用采用lxml庫來定位獲取需要的參數疾渣;
我們以Phpwind登陸的來進行舉例,在登陸的接口中需要把token參數傳給服務器崖飘,token的值由頁的接口返回榴捡;
方法一:使用正則表達式
方法二:采用lxml庫來定位獲取需要的參數
技術點:
1、導模塊:lxml模塊
2朱浴、etree.HTML() 從返回html頁面獲取html文件的dom結構
3吊圾、xpath() 獲取token的xpath路徑
Locust中實現斷言
from locust import HttpLocust, TaskSet, task
class UserTask(TaskSet):
@task
def job(self):
with self.client.get('/', catch_response = True) as response:
if response.status_code == 200:
response.failure('Failed!')
else:
response.success()
class User(HttpUser):
task_create = UserTask
min_wait = 1000
max_wait = 3000
host = "https://www.baidu.com"
catch_response = True :布爾類型达椰,如果設置為 True, 允許該請求被標記為失敗。
通過 client.get() 方法發(fā)送請求街夭,將整個請求的給 response砰碴, 通過 response.status_code 得請求響應的 HTTP 狀態(tài)碼。如果不為 200 則通過 response.failure('Failed!') 打印失敯謇觥呈枉!
參考文章:
http://www.reibang.com/p/a48f4af81e67
https://www.cnblogs.com/fnng/p/6081798.html
http://class.itest.info/locust【蟲師】
https://cloud.tencent.com/developer/article/1594240【官方文檔的中文翻譯】