轉(zhuǎn)自 (https://www.cnblogs.com/qingchengzi/p/9505548.html)
啟動(dòng):
終端中--->進(jìn)入到代碼目錄: locust -f xxxoo.py --host=xxxxx.com
- -f 指定性能測(cè)試腳本文件
- -host 被測(cè)試應(yīng)用的URL地址【如果不填寫,讀取繼承(HttpLocust)類中定義的host】
- 通過(guò)瀏覽器訪問:http://localhost:8089(Locust啟動(dòng)網(wǎng)絡(luò)監(jiān)控器,默認(rèn)為端口號(hào)為:8089)

Number of users to simulate 設(shè)置虛擬用戶數(shù)
Hatch rate(users spawned/second)每秒產(chǎn)生(啟動(dòng))的虛擬用戶數(shù)
點(diǎn)擊Start swarming 按鈕,開始運(yùn)行性能測(cè)試。
no-web模式運(yùn)行啟動(dòng)
終端中-->進(jìn)入代碼目錄:>> locust -f xxoo.py --no-web -c10 -r2 -t 1m
啟動(dòng)參數(shù):
--no-web 表示不使用web界面運(yùn)行測(cè)試。 -c 設(shè)置虛擬用戶數(shù) 。 -r 設(shè)置每秒啟動(dòng)虛擬用戶數(shù) 。 -t 設(shè)置運(yùn)行時(shí)間.。
no-web模式運(yùn)行將測(cè)試結(jié)果保存到當(dāng)前.py目錄中:locust -f xxoo.py --csv=起一個(gè)名字
例如:
locust -f test3.py --csv=foobar --no-web -c2 -t10s
分布式壓測(cè):
主從機(jī)中必須運(yùn)行相同的測(cè)試代碼(把主機(jī)中代碼復(fù)制一份到多個(gè)從機(jī)中),主機(jī)負(fù)責(zé)收集測(cè)試數(shù)據(jù),從機(jī)進(jìn)行施壓測(cè)試;
在主機(jī)終端中-->進(jìn)入代碼目錄:>> locust -f xxxoo.py --master
從機(jī)中終端中-->進(jìn)入代碼目錄:>> locust -f xxxoo.py --slave --master-host=主機(jī)ip
分布式壓測(cè)no-web模式保存結(jié)果到主機(jī)中當(dāng)前運(yùn)行.py的目錄中:>>locust -f test2.py --csv=foobartt --no-web -c2 -t10s --master
locust --help 查看幫助信息
概述:
Locust寓意蝗蟲,蝗蟲過(guò)境,寸草不生;而Locust工具生成并發(fā)請(qǐng)求就和一大群蝗蟲一般,向我們的被測(cè)系統(tǒng)發(fā)起攻擊,以此測(cè)試系統(tǒng)在高并發(fā)壓力下是否能正常運(yùn)轉(zhuǎn)。
Locust測(cè)試框架中,采用python進(jìn)行開發(fā),對(duì)常見的http(s)協(xié)議的系統(tǒng),Locust采用request庫(kù)作為客戶端,在發(fā)請(qǐng)求時(shí)和request庫(kù)使用方法一樣。
在模擬并發(fā)時(shí),Locust采用協(xié)程、非阻塞IO來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)層的并發(fā)請(qǐng)求,因此單臺(tái)壓力機(jī)也能產(chǎn)生數(shù)千并發(fā)請(qǐng)求,再加上對(duì)分布式運(yùn)行的支持,Locust能在使用較少壓力機(jī)的前提下支持極高的并發(fā)數(shù)測(cè)試。
實(shí)例腳本
from locust import HttpLocust, TaskSet, task
class WebsiteTasks(TaskSet):
def on_start(self): #進(jìn)行初始化的工作,每個(gè)Locust用戶開始做的第一件事
payload = {
"username": "test_user",
"password": "123456",
}
header = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
}
self.client.post("/login",data=payload,headers=header)#self.client屬性使用Python request庫(kù)的所有方法,調(diào)用和使用方法和requests完全一致;
@task(5) #通過(guò)@task()裝飾的方法為一個(gè)事務(wù),方法的參數(shù)用于指定該行為的執(zhí)行權(quán)重,參數(shù)越大每次被虛擬用戶執(zhí)行的概率越高,默認(rèn)為1
def index(self):
self.client.get("/")
@task(1)
def about(self):
self.client.get("/about/")
class WebsiteUser(HttpLocust):
host = "https://github.com/" #被測(cè)系統(tǒng)的host,在終端中啟動(dòng)locust時(shí)沒有指定--host參數(shù)時(shí)才會(huì)用到
task_set = WebsiteTasks #TaskSet類,該類定義用戶任務(wù)信息,必填。這里就是:WebsiteTasks類名,因?yàn)樵擃惱^承TaskSet;
min_wait = 5000 #每個(gè)用戶執(zhí)行兩個(gè)任務(wù)間隔時(shí)間的上下限(毫秒),具體數(shù)值在上下限中隨機(jī)取值,若不指定默認(rèn)間隔時(shí)間固定為1秒
max_wait = 15000
偽代碼中對(duì)https://github.com/網(wǎng)站的測(cè)試場(chǎng)景,先模擬用戶登錄系統(tǒng),然后隨機(jī)訪問首頁(yè)/和/about/,請(qǐng)求比例5:1,并且在測(cè)試過(guò)程中,兩次請(qǐng)求的間隔時(shí)間1-5秒的隨機(jī)值;
on_start方法,在正式執(zhí)行測(cè)試前執(zhí)行一次,主要用于完成一些初始化的工作,例如登錄操作;
WebsiteTasks類中如何去調(diào)用 WebsiteUser(HttpLocust)類中定義的字段和方法呢?
通過(guò)在WebsiteTasks類中self.locust.xxoo xxoo就是我們?cè)赪ebsiteUser類中定義的字段或方法;
偽代碼:
from locust import HttpLocust, TaskSet, task
import hashlib
import queue
class WebsiteTasks(TaskSet):
@task(5)
def index(self):
data = self.locust.user_data_queue #獲取WebsiteUser里面定義的ser_data_queue隊(duì)列
md5_data=self.locust.md5_encryption() #獲取WebsiteUser里面定義的md5_encryption()方法
self.client.get("/")
class WebsiteUser(HttpLocust):
host = "https://github.com/"
task_set = WebsiteTasks
min_wait = 5000
max_wait = 15000
user_data_queue = queue.Queue()
def md5_encryption(self,star):
'''md5加密方法'''
obj = hashlib.md5()
obj.update(bytes(star,encoding="utf-8"))
result = obj.hexdigest()
return result
偽代碼中測(cè)試場(chǎng)景如何表達(dá)?
代碼主要包含兩個(gè)類:
WebsiteUser繼承(HttpLocust,而HttpLocust繼承自Locust)
WebsiteTasks繼承(TaskSet)
在Locust測(cè)試腳本中,所有業(yè)務(wù)測(cè)試場(chǎng)景都是在Locust和TaskSet兩個(gè)類的繼承子類中進(jìn)行描述;
簡(jiǎn)單說(shuō):Locust類就類似一群蝗蟲,而每只蝗蟲就是一個(gè)類的實(shí)例。TaskSet類就類似蝗蟲的大腦,控制蝗蟲的具體行為,即實(shí)際業(yè)務(wù)場(chǎng)景測(cè)試對(duì)應(yīng)的任務(wù)集;
在Locust類中,靜態(tài)字段client即客戶端的請(qǐng)求方法,這里的client字段沒有綁定客戶端請(qǐng)求方法,因此在使用Locust時(shí),需要先繼承Locust類class HttpLocust(Locust),然后在self.client = HttpSession(base_url=self.host)綁定客戶端請(qǐng)求方法;
對(duì)于常見的HTTP(s)協(xié)議,Locust已經(jīng)實(shí)現(xiàn)了HttpLocust類,其self.client=HttpSession(base_url=self.host),而HttpSession繼承自requests.Session。因此在測(cè)試HTTP(s)的Locust腳本中,可以通過(guò)client屬性來(lái)使用Python requests庫(kù)的所 有方法,調(diào)用方式與 reqeusts完全一致。另外,由于requests.Session的使用,client的方法調(diào)用之間就自動(dòng)具有了狀態(tài)記憶功能。常見的場(chǎng)景就是,在登錄系統(tǒng)后可以維持登錄狀態(tài)的Session,從而后續(xù)HTTP請(qǐng)求操作都能帶上登錄狀態(tài);
Locust類中,除了client屬性,還有幾個(gè)屬性需要關(guān)注:
task_set ---> 指向一個(gè)TaskSet類,TaskSet類定義了用戶的任務(wù)信息,該靜態(tài)字段為必填;
max_wait/min_wait ---> 每個(gè)用戶執(zhí)行兩個(gè)任務(wù)間隔的上下限(毫秒),具體數(shù)值在上下限中隨機(jī)取值,若不指定則默認(rèn)間隔時(shí)間為1秒;
host --->被測(cè)試系統(tǒng)的host,當(dāng)在終端中啟動(dòng)locust時(shí)沒有指定--host參數(shù)時(shí)才會(huì)用到;
weight--->同時(shí)運(yùn)行多個(gè)Locust類時(shí),用于控制不同類型的任務(wù)執(zhí)行權(quán)重;
Locust流程,測(cè)試開始后,每個(gè)虛擬用戶(Locust實(shí)例)運(yùn)行邏輯都會(huì)遵守如下規(guī)律:
先執(zhí)行WebsiteTasks中的on_start(只執(zhí)行一次),作為初始化;
從WebsiteTasks中隨機(jī)挑選(如果定義了任務(wù)間的權(quán)重關(guān)系,那么就按照權(quán)重關(guān)系隨機(jī)挑選)一個(gè)任務(wù)執(zhí)行;
根據(jù)Locust類中min_wait和max_wait定義的間隔時(shí)間范圍(如果TaskSet類中也定義了min_wait或者max_wait,以TaskSet中的優(yōu)先),在時(shí)間范圍中隨機(jī)取一個(gè)值,休眠等待;
重復(fù)2~3步驟,直到測(cè)試任務(wù)終止;
TaskSet類實(shí)現(xiàn)了虛擬用戶所執(zhí)行任務(wù)的調(diào)度算法,包括規(guī)劃任務(wù)執(zhí)行順序(schedule_task)、挑選下一個(gè)任務(wù)(execute_next_task)、執(zhí)行任務(wù)(execute_task)、休眠等待(wait)、中斷控制(interrupt)等待。在此基礎(chǔ)上,就可以在TaskSet子類中采用非常簡(jiǎn)潔的方式來(lái)描述虛擬用戶的業(yè)務(wù)測(cè)試場(chǎng)景,對(duì)虛擬用戶的所有行為進(jìn)行組織和描述,并可以對(duì)不同任務(wù)的權(quán)重進(jìn)行配置。
@task
通過(guò)@task()裝飾的方法為一個(gè)事務(wù)。方法的參數(shù)用于指定該行為的執(zhí)行權(quán)重。參數(shù)越大每次被虛擬用戶執(zhí)行的概率越高。如果不設(shè)置默認(rèn)為1。
TaskSet子類中定義任務(wù)信息時(shí),采取兩種方式:@task裝飾器和tasks屬性。
采用@task裝飾器定義任務(wù)信息時(shí):
from locust import TaskSet, task
class UserBehavior(TaskSet):
@task(1)
def test_job1(self):
self.client.get('/test1')
@task(3)
def test_job2(self):
self.client.get('/test2')
采用tasks屬性定義任務(wù)信息時(shí)
from locust import TaskSet
def test_job1(obj):
obj.client.get('/test1')
def test_job2(obj):
obj.client.get('/test2')
class UserBehavior(TaskSet):
tasks = {test_job1:1, test_job2:3}
# tasks = [(test_job1,1), (test_job1,3)] # 兩種方式等價(jià)
上面兩種定義任務(wù)信息方式中,均設(shè)置了權(quán)重屬性,即執(zhí)行test_job2的頻率是test_job1的兩倍。
若不指定,默認(rèn)比例為1:1。
關(guān)聯(lián):
在某些請(qǐng)求中,需要攜帶之前response中提取的參數(shù),常見場(chǎng)景就是session_id。Python中可用通過(guò)re正則匹配,對(duì)于返回的html頁(yè)面,可用采用lxml庫(kù)來(lái)定位獲取需要的參數(shù);
from locust import HttpLocust, TaskSet, task
from lxml import etree
class WebsiteTasks(TaskSet):
def get_session(self,html): #關(guān)聯(lián)例子
tages = etree.HTML(html)
return tages.xpath("http://div[@class='btnbox']/input[@name='session']/@value")[0]
def on_start(self):
html = self.client.get('/index')
session = self.get_session(html.text)
payload = {
"username": "test_user",
"password": "123456",
'session' : session
}
header = {
"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
}
self.client.post("/login",data=payload,headers=header)
@task(5)
def index(self):
self.client.get("/")
assert response['ErrorCode']==0 #斷言
@task(1)
def about(self):
self.client.get("/about/")
class WebsiteUser(HttpLocust):
host = "https://github.com/"
task_set = WebsiteTasks
min_wait = 5000
max_wait = 15000