Locust文件就是一般的Python文件。唯一的需求就是它至少需要一個繼承于Locust
的類.
Locust類
Locust類代表一個用戶(如果愿意忌愚,也可以是一個準(zhǔn)備出動的蝗蟲)舔涎。Locust會為每一個模擬用戶生成一個locust類實(shí)例秒拔。同時會有一些locust類屬性被定義顽素。
task_set
屬性
task_set
屬性是指向一個定義用戶行為的TaskSet
類础倍,下面會有詳細(xì)的介紹烛占。
min_wait
和max_wait
屬性
除了task_set
屬性,另外一個經(jīng)常被使用的就是min_wait
和max_wait
屬性。是用于各自以毫秒為單位的最小值和最大值忆家,一個模擬用戶將會在每個任務(wù)執(zhí)行時的等待執(zhí)行的時間間隔犹菇。min_wait
和max_wait
默認(rèn)設(shè)置為1000
,如果不聲明的話芽卿,Locust會默認(rèn)在每個任務(wù)間等待1秒
揭芍。
參考下面的代碼,每個用戶將會在每個任務(wù)間等待5至15
秒:
from locust import Locust, TaskSet, task_set
class MyTaskSet(TaskSet):
@task
def my_task(self):
print "executing my_task"
class MyLocust(Locust):
task_set = MyTaskSet
min_wait = 5000
max_wait = 15000
min_wait
和max_wait
屬性可以用于重寫TaskSet
類卸例。
weight
屬性
你可以通過同一個文件來運(yùn)行兩個locust称杨,就像這樣:
locust -f locust_file.py WebUserLocust MobileUserLocust
如果你更傾向于用這種方法來運(yùn)行,便可以在這些類中嘗試weight
屬性筷转。比如姑原,就像這樣來定義web用戶比Mobile用戶多3倍
:
class WebUserLocust(Locust):
weight = 3
...
class MobileUserLocust(Locust):
weight = 1
...
host
屬性
host
屬性是到要加載目標(biāo)URL
的前綴(如:"http://google.com")。通常情況下呜舒,當(dāng)Locust被啟動時锭汛,在命令行中是需要通過--host
來指定的。如果host
屬性在locustfile文件中被聲明袭蝗,則在命令行中則不需要使用--host
屬性來再次聲明店乐。
TaskSet
類
如果Locust類代表一只準(zhǔn)備出動的蝗蟲,那么你可以說TaskSet
類代表蝗蟲的大腦呻袭。每一個Locust類中必須要包含一個指向TaskSet
的task_set
屬性設(shè)置。
TaskSet
就像它的名字一樣腺兴,是一個任務(wù)集合左电。這些任務(wù)是常規(guī)的Python調(diào)用,如果我們壓力測試一個拍賣網(wǎng)站,便可以做這些操作加載啟動頁面
页响、搜索一些產(chǎn)品
篓足、競標(biāo)
。
當(dāng)一個壓力測試被啟動時闰蚕,每一個準(zhǔn)備的Locust類實(shí)例將會開始執(zhí)行它們的TaskSet
栈拖。接下來是每一個TaskSet
找到它的task
并調(diào)用它。它將在min_wait
和max_wait
屬性值之間隨機(jī)等待幾毫秒(除非min_wait
和max_wait
被定義在TaskSet中没陡,在這種情況下將會使用TaskSet設(shè)置的值)涩哟。然后,它將會找到一個新task
并調(diào)用盼玄,再次等待贴彼,一直這樣持續(xù)下去。
聲明task
對于TaskSet
來說埃儿,典型的聲明task
的方法是直接使用task
器仗。
參考這個例子:
from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
@task
def my_task(self):
print "Locust instance (%r) executing my_task" % (self.locust)
class MyLocust(Locust):
task_set = MyTaskSet
@task 將會獲取一個可選的權(quán)重參數(shù),用于說明任務(wù)執(zhí)行的比率。在下面的例子中 task2 將會比 task1 執(zhí)行的次數(shù)多兩倍:
from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
min_wait = 5000
max_wait = 15000
@task(3)
def task1(self):
pass
@task(6)
def task2(self):
password
class MyLocust(Locust):
task_set = MyTaskSet
task
屬性
使用@task
操作符來聲明task是一種便捷的方法精钮,并且經(jīng)常是最好的方式威鹿。然而,也可以定義TaskSet
中的task通過設(shè)置tasks屬性(使用操作符@task比tasks屬性更流行)轨香。
tasks 屬性不是python列表的調(diào)用就是一個<callbale:int>字典忽你。tasks是python調(diào)用接收執(zhí)行task的TaskSet類實(shí)例參數(shù)。下面是一個極其簡單的示例(不會影響任何測試):
from locust import Locust, TaskSet
def my_task(l):
pass
class MyTaskSet(TaskSet):
tasks = [my_task]
class MyLocust(Locust):
task_set = MyTaskSet
如果task屬性被定義在列表中弹沽,每次任務(wù)被執(zhí)行時檀夹,將會隨機(jī)
從 tasks 屬性中選擇。如果 tasks 是一個帶有關(guān)健字和數(shù)值調(diào)用的字典策橘,被執(zhí)行的任務(wù)將會被隨機(jī)選擇以數(shù)字的比率來執(zhí)行炸渡。就像下面的這樣:
{my_task: 3, another_task:1}
my_task 將會比 another_task 多執(zhí)行三倍。
TaskSet
可以嵌套
TaskSet
有一個重要的屬性就是可以被嵌套丽已,由于真實(shí)的網(wǎng)站是有一定的業(yè)務(wù)層級結(jié)構(gòu)的蚌堵,并帶有一些子模塊。嵌套的TaskSet將會幫助我們來定義更加真實(shí)的用戶行為沛婴。比如吼畏,我們可以定義TaskSet
像下面的結(jié)構(gòu)
- Main user behaviour
- Index page
- Forum page
- Read thread
- Reply
- New thread
- View next page
- Read thread
- Browser categories
- Watch movies
- Filter movies
- About page
嵌套TaskSet的方法就像使用task
屬性來說明task一樣,但代替參考Python函數(shù)嘁灯,你可以參考下面的TaskSet
:
class ForumPage(TaskSet):
@task(20)
def read_thread(self):
pass
@task(1)
def new_thread(self):
pass
@task(5)
def stop(self):
self.interrupt()
class UserBehaviour(TaskSet):
tasks = {ForumPage:10}
@task
def index(self):
pass
在上面的示例中泻蚊,當(dāng)UserBehaviour的TaskSet執(zhí)行時,F(xiàn)orumPage會被選中來執(zhí)行丑婿,接下來ForumPage的TaskSet將會開始執(zhí)行性雄。ForumPage的TaskSet會找到它的tasks并執(zhí)行它,再等待羹奉,一直這樣持續(xù)下去秒旋。
針對上面的例子中有一個重要的事情要注意,就是在ForumPage頁面中的Stop方法中調(diào)用self.interrupt()
诀拭。這個做的事情是停止執(zhí)行ForumPage任務(wù)并在UserBehaviour實(shí)例中繼續(xù)執(zhí)行迁筛。如果在ForumPage中,我們沒有調(diào)用interrupt()
方法耕挨,除非被調(diào)用否則Locust不會調(diào)用ForumPage任務(wù)细卧。但通過interrupt
函數(shù) ,我們可以結(jié)合weight
任務(wù)來定義模擬用戶離開Forum.
也可以在類內(nèi)部聲明嵌套TaskSet,通過使用@task
操作符筒占,像聲明正常的task一樣:
class MyTaskSet(TaskSet):
@task
class SubTaskSet(TaskSet):
@task
def my_task(self):
pass
on_start
函數(shù)
TaskSet可以選擇聲明on_start
函數(shù)酒甸。如果這樣的話,當(dāng)模擬用戶開始執(zhí)行TaskSet類時赋铝,函數(shù)被調(diào)用插勤。
關(guān)聯(lián)Locust
實(shí)例,或父TaskSet
實(shí)例
TaskSet
實(shí)例有locust
屬性來指向它的Locust
實(shí)例,屬性parent
用來指向它的父TaskSet
(它會指向Locsut實(shí)例农尖,在基類TaskSet中)析恋。
HTTP
請求
到現(xiàn)在為止,我們僅覆蓋了一個Locsut用戶的部分任務(wù)計(jì)劃盛卡。為了真實(shí)的壓力測試一個系統(tǒng)時助隧,我們需要生成HTTP
請求。為了幫助我們實(shí)現(xiàn)這個功能滑沧,可以使用HttpLocust
類并村。當(dāng)使用這個類時,每一個實(shí)例將會獲得一個用于生成Http
請求的HttpSession
實(shí)例的client
屬性滓技。
class HttpLocust
表示一個用于壓力測試的孵化和攻擊系統(tǒng)的HTTP
用戶
哩牍。
這個用戶的行為通過task_set
屬性來定義,直接指向TaskSet
類令漂。
這個類創(chuàng)建一個client
屬性膝昆,在初始化時,HTTP
客戶端支持為每一個用戶在請求間保存session叠必。
client=None
HttpSession實(shí)例在Locust初始化時被創(chuàng)建荚孵。client
支持cookies
,同時在請求間會保存session纬朝。
當(dāng)從HttpLocust
類繼承時收叶,我們可以使用client
屬性來對服務(wù)器生成HTTP
請求。下面是一個locust文件示例用于在一個網(wǎng)站的兩個URL / 和 /about/ 共苛。
from locust import HttpLocust, TaskSet, task
class MyTaskSet(TaskSet):
@task(2)
def index(self):
self.client.get('/')
@task(1)
def about(self):
self.client.get('/about/')
class MyLocust(HttpLocust):
task_set = MyTaskSet
min_wait = 5000
max_wait = 15000
使用上面的Locust類判没,每一個模擬用戶將間隔5-15秒內(nèi)請求,并且/
將會比/about/
請求數(shù)量多2倍
俄讹。
細(xì)心的讀者會發(fā)現(xiàn)有一些奇怪,我們使用self.client
關(guān)聯(lián)HttpSession
實(shí)例绕德,而不是TaskSet
,也不是self.locust.client
患膛。我們可以這樣做,是因?yàn)?code>TaskSet類有一個屬性調(diào)用client
簡單的返回self.locust.client
耻蛇。
使用HTTP client
每一個HttpLocust
實(shí)例在client
屬性中有一個HttpSession
實(shí)例踪蹬。HttpSession
類實(shí)際上是requests.Session
的子類,可使用get
post
put
delete
head
patch
和 options
方法來生成HTTP
請求臣咖,用于Locust的數(shù)據(jù)統(tǒng)計(jì)跃捣。HttpSession
實(shí)例在請求間維護(hù)cookies,因此可用于登錄網(wǎng)站并保存session在請求之間夺蛇。client
可以通過Locust實(shí)例的TaskSet實(shí)例來關(guān)聯(lián)疚漆,因此很容易獲取client并在任務(wù)中生成HTTP請求。
下面是一個生成GET
請求到 /about 路徑的示例(在這里,我們可以假設(shè) self 是一個TaskSet
或 HttpLocust
類的實(shí)例):
response = self.client.get("/about")
print "Response staus code:", response.status_code
print "Response content:", response.content
下面是一個生成POST
請求的示例:
response = self.client.post("/login", {"username": "testuser", "password": "password"})
安全模式
HTTP
client被配制運(yùn)行在safe_mode
娶聘。這樣做是任何請求在連接超時闻镶、錯誤、相似失敗時將不會拋出異常丸升,而是返回一個空的假Response對象铆农。請求將會在Locust統(tǒng)計(jì)中算做一次失敗。返回假Response內(nèi)容屬性將會被設(shè)置為None狡耻,并且它的status_code將會是0.
手動設(shè)置請求是成功或失敗
默認(rèn)情況下墩剖,請求被標(biāo)記為失敗除非在返回狀態(tài)碼是OK(2XX)。大部分時間內(nèi)夷狰,這個默認(rèn)就是你所需要的岭皂。然而,比如在測試一個URL節(jié)點(diǎn)孵淘,你期待返回狀態(tài)碼為404蒲障,或者測試一個即使錯誤發(fā)生也會返回200的系統(tǒng),因此瘫证,需要手工控制locust來判斷是成功還是失敗揉阎。
一個可以生成失敗請求,即使當(dāng)響應(yīng)代碼是OK
背捌,通過使用catch_response
參數(shù)和with
語法:
with client.get("/", catch_response = True) as response:
if response.content != "Success":
response.failure("Got wrong response")
就像一個可以使用響應(yīng)為OK
的請求當(dāng)做失敗來處理毙籽,一個方法就是可以使用catch_response
參數(shù)和with
語法來讓請求HTTP錯誤時,仍然統(tǒng)計(jì)數(shù)據(jù)為成功:
with client.get("/does_not_exist/", catch_response = True) as response:
if response.status_code = 404:
response.success()
使用動態(tài)參數(shù)來分組URL請求
針對網(wǎng)站毡庆,有一個常用的功能是獲取URL中包含一些動態(tài)參數(shù)的頁面數(shù)據(jù)坑赡。通常情況下,在Locust統(tǒng)計(jì)中么抗,使用動態(tài)分組在URL中是很有意義的毅否。通過name
參數(shù)來給HttpSession
傳遞不同的請求方法。
比如:
# Statistics for these requests will be grouped under: /blog/?id=[id]
for i in range(10):
client.get("/blog?id=%i" % i, name = "/blog?id=[id]")
常用庫
通常蝇刀,大家想分享多個locust文件用于分享常用的庫螟加。在這種情況下,定義項(xiàng)目根目錄
用于調(diào)用Locsut是很重要的吞琐,建議將所有的locust文件有些話在項(xiàng)目的根目錄中捆探。
一個平鋪的結(jié)構(gòu)像下面這樣:
- 項(xiàng)目根目錄
commonlib_conf.py
commonlib_auth.py
locustfile_web_app.py
locsutfile_api.py
locustfile_ecommerce.py
locust文件可以調(diào)用常用的庫通過使用import commonlib_auth
.然而,這種方法不會從locust文件中站粟,清晰分辨出常用庫黍图。
子文件夾可以有一個清晰的方法(查看下面的示例),但是locust僅會有運(yùn)行l(wèi)ocsut文件的位置引用相關(guān)的模塊奴烙。如果你想從你的根目錄導(dǎo)入(如助被,你運(yùn)行l(wèi)ocust命令的位置)剖张,確保在任何locust文件中添加常用庫前有代碼sys.path.append(os.getcwd())
,會生成導(dǎo)入根目錄(如恰起,當(dāng)前工作目錄)修械。
- project root
__init__.py
-
common/
__init__.py
config.py
auth.py
-
locustfiles/
__init__.py
web_app.py
api.py
ecommerce.py
使用上面的項(xiàng)目結(jié)構(gòu),你的locust文件可以通過下面代碼導(dǎo)入常用的庫:
sys.path.append(os.getcwd())
import common.auth