性能测试: 编写一个 Locust 文件
Locust文件就是一般的Python文件。唯一的需求就是它至少需要一個(gè)繼承于Locust的類.
Locust類
Locust類代表一個(gè)用戶(如果愿意,也可以是一個(gè)準(zhǔn)備出動(dòng)的蝗蟲)。Locust會(huì)為每一個(gè)模擬用戶生成一個(gè)locust類實(shí)例。同時(shí)會(huì)有一些locust類屬性被定義。
task_set屬性
task_set屬性是指向一個(gè)定義用戶行為的TaskSet類,下面會(huì)有詳細(xì)的介紹。
min_wait和max_wait屬性
除了task_set屬性,另外一個(gè)經(jīng)常被使用的就是min_wait和max_wait屬性。是用于各自以毫秒為單位的最小值和最大值,一個(gè)模擬用戶將會(huì)在每個(gè)任務(wù)執(zhí)行時(shí)的等待執(zhí)行的時(shí)間間隔。min_wait和max_wait默認(rèn)設(shè)置為1000,如果不聲明的話,Locust會(huì)默認(rèn)在每個(gè)任務(wù)間等待1秒。
參考下面的代碼,每個(gè)用戶將會(huì)在每個(gè)任務(wù)間等待5至15秒:
from locust import Locust, TaskSet, task_setclass MyTaskSet(TaskSet):@taskdef my_task(self):print "executing my_task"class MyLocust(Locust):task_set = MyTaskSetmin_wait = 5000max_wait = 15000復(fù)制代碼min_wait和max_wait屬性可以用于重寫TaskSet類。
weight屬性
你可以通過同一個(gè)文件來(lái)運(yùn)行兩個(gè)locust,就像這樣:
locust -f locust_file.py WebUserLocust MobileUserLocust復(fù)制代碼如果你更傾向于用這種方法來(lái)運(yùn)行,便可以在這些類中嘗試weight屬性。比如,就像這樣來(lái)定義web用戶比Mobile用戶多3倍:
class WebUserLocust(Locust):weight = 3...class MobileUserLocust(Locust):weight = 1...復(fù)制代碼host屬性
host屬性是到要加載目標(biāo)URL的前綴(如:"google.com")。通常情況下,當(dāng)Locust被啟動(dòng)時(shí),在命令行中是需要通過--host來(lái)指定的。如果host屬性在locustfile文件中被聲明,則在命令行中則不需要使用--host屬性來(lái)再次聲明。
TaskSet類
如果Locust類代表一只準(zhǔn)備出動(dòng)的蝗蟲,那么你可以說TaskSet類代表蝗蟲的大腦。每一個(gè)Locust類中必須要包含一個(gè)指向TaskSet的task_set屬性設(shè)置。
TaskSet就像它的名字一樣,是一個(gè)任務(wù)集合。這些任務(wù)是常規(guī)的Python調(diào)用,如果我們壓力測(cè)試一個(gè)拍賣網(wǎng)站,便可以做這些操作加載啟動(dòng)頁(yè)面、搜索一些產(chǎn)品、競(jìng)標(biāo)。
當(dāng)一個(gè)壓力測(cè)試被啟動(dòng)時(shí),每一個(gè)準(zhǔn)備的Locust類實(shí)例將會(huì)開始執(zhí)行它們的TaskSet。接下來(lái)是每一個(gè)TaskSet找到它的task并調(diào)用它。它將在min_wait和max_wait屬性值之間隨機(jī)等待幾毫秒(除非min_wait和max_wait被定義在TaskSet中,在這種情況下將會(huì)使用TaskSet設(shè)置的值)。然后,它將會(huì)找到一個(gè)新task并調(diào)用,再次等待,一直這樣持續(xù)下去。
聲明task
對(duì)于TaskSet來(lái)說,典型的聲明task的方法是直接使用task。
參考這個(gè)例子:
from locust import Locust, TaskSet, task class MyTaskSet(TaskSet):@taskdef my_task(self):print "Locust instance (%r) executing my_task" % (self.locust)class MyLocust(Locust):task_set = MyTaskSet復(fù)制代碼@task 將會(huì)獲取一個(gè)可選的權(quán)重參數(shù),用于說明任務(wù)執(zhí)行的比率。在下面的例子中 task2 將會(huì)比 task1 執(zhí)行的次數(shù)多兩倍:
from locust import Locust, TaskSet, task class MyTaskSet(TaskSet):min_wait = 5000max_wait = 15000@task(3)def task1(self):pass@task(6)def task2(self):passwordclass MyLocust(Locust):task_set = MyTaskSet復(fù)制代碼task屬性
使用@task操作符來(lái)聲明task是一種便捷的方法,并且經(jīng)常是最好的方式。然而,也可以定義TaskSet中的task通過設(shè)置tasks屬性(使用操作符@task比tasks屬性更流行)。
tasks 屬性不是python列表的調(diào)用就是一個(gè)字典。tasks是python調(diào)用接收?qǐng)?zhí)行task的TaskSet類實(shí)例參數(shù)。下面是一個(gè)極其簡(jiǎn)單的示例(不會(huì)影響任何測(cè)試):
from locust import Locust, TaskSetdef my_task(l):passclass MyTaskSet(TaskSet):tasks = [my_task]class MyLocust(Locust):task_set = MyTaskSet復(fù)制代碼如果task屬性被定義在列表中,每次任務(wù)被執(zhí)行時(shí),將會(huì)隨機(jī)從 tasks 屬性中選擇。如果 tasks 是一個(gè)帶有關(guān)健字和數(shù)值調(diào)用的字典,被執(zhí)行的任務(wù)將會(huì)被隨機(jī)選擇以數(shù)字的比率來(lái)執(zhí)行。就像下面的這樣:
{my_task: 3, another_task:1}復(fù)制代碼my_task 將會(huì)比 another_task 多執(zhí)行三倍。
TaskSet可以嵌套
TaskSet有一個(gè)重要的屬性就是可以被嵌套,由于真實(shí)的網(wǎng)站是有一定的業(yè)務(wù)層級(jí)結(jié)構(gòu)的,并帶有一些子模塊。嵌套的TaskSet將會(huì)幫助我們來(lái)定義更加真實(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屬性來(lái)說明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}@taskdef index(self):pass復(fù)制代碼在上面的示例中,當(dāng)UserBehaviour的TaskSet執(zhí)行時(shí),ForumPage會(huì)被選中來(lái)執(zhí)行,接下來(lái)ForumPage的TaskSet將會(huì)開始執(zhí)行。ForumPage的TaskSet會(huì)找到它的tasks并執(zhí)行它,再等待,一直這樣持續(xù)下去。
針對(duì)上面的例子中有一個(gè)重要的事情要注意,就是在ForumPage頁(yè)面中的Stop方法中調(diào)用self.interrupt()。這個(gè)做的事情是停止執(zhí)行ForumPage任務(wù)并在UserBehaviour實(shí)例中繼續(xù)執(zhí)行。如果在ForumPage中,我們沒有調(diào)用interrupt()方法,除非被調(diào)用否則Locust不會(huì)調(diào)用ForumPage任務(wù)。但通過interrupt函數(shù) ,我們可以結(jié)合weight任務(wù)來(lái)定義模擬用戶離開Forum.
也可以在類內(nèi)部聲明嵌套TaskSet,通過使用@task操作符,像聲明正常的task一樣:
class MyTaskSet(TaskSet):@taskclass SubTaskSet(TaskSet):@taskdef my_task(self):pass復(fù)制代碼on_start函數(shù)
TaskSet可以選擇聲明on_start函數(shù)。如果這樣的話,當(dāng)模擬用戶開始執(zhí)行TaskSet類時(shí),函數(shù)被調(diào)用。
關(guān)聯(lián)Locust實(shí)例,或父TaskSet實(shí)例
TaskSet實(shí)例有l(wèi)ocust屬性來(lái)指向它的Locust實(shí)例,屬性parent用來(lái)指向它的父TaskSet(它會(huì)指向Locsut實(shí)例,在基類TaskSet中)。
HTTP請(qǐng)求
到現(xiàn)在為止,我們僅覆蓋了一個(gè)Locsut用戶的部分任務(wù)計(jì)劃。為了真實(shí)的壓力測(cè)試一個(gè)系統(tǒng)時(shí),我們需要生成HTTP請(qǐng)求。為了幫助我們實(shí)現(xiàn)這個(gè)功能,可以使用HttpLocust類。當(dāng)使用這個(gè)類時(shí),每一個(gè)實(shí)例將會(huì)獲得一個(gè)用于生成Http請(qǐng)求的HttpSession實(shí)例的client屬性。
class HttpLocust復(fù)制代碼表示一個(gè)用于壓力測(cè)試的孵化和攻擊系統(tǒng)的HTTP 用戶。
這個(gè)用戶的行為通過task_set屬性來(lái)定義,直接指向TaskSet類。
這個(gè)類創(chuàng)建一個(gè)client屬性,在初始化時(shí),HTTP客戶端支持為每一個(gè)用戶在請(qǐng)求間保存session。
client=None復(fù)制代碼HttpSession實(shí)例在Locust初始化時(shí)被創(chuàng)建。client支持cookies,同時(shí)在請(qǐng)求間會(huì)保存session。
當(dāng)從HttpLocust類繼承時(shí),我們可以使用client屬性來(lái)對(duì)服務(wù)器生成HTTP請(qǐng)求。下面是一個(gè)locust文件示例用于在一個(gè)網(wǎng)站的兩個(gè)URL / 和 /about/ 。
from locust import HttpLocust, TaskSet, taskclass MyTaskSet(TaskSet):@task(2)def index(self):self.client.get('/')@task(1)def about(self):self.client.get('/about/') class MyLocust(HttpLocust):task_set = MyTaskSetmin_wait = 5000max_wait = 15000復(fù)制代碼使用上面的Locust類,每一個(gè)模擬用戶將間隔5-15秒內(nèi)請(qǐng)求,并且/將會(huì)比/about/請(qǐng)求數(shù)量多2倍。
細(xì)心的讀者會(huì)發(fā)現(xiàn)有一些奇怪,我們使用self.client關(guān)聯(lián)HttpSession實(shí)例,而不是TaskSet,也不是self.locust.client。我們可以這樣做,是因?yàn)門askSet類有一個(gè)屬性調(diào)用client簡(jiǎn)單的返回self.locust.client。
使用HTTP client
每一個(gè)HttpLocust實(shí)例在client屬性中有一個(gè)HttpSession實(shí)例。HttpSession類實(shí)際上是requests.Session的子類,可使用get post put delete head patch 和 options方法來(lái)生成HTTP請(qǐng)求,用于Locust的數(shù)據(jù)統(tǒng)計(jì)。HttpSession實(shí)例在請(qǐng)求間維護(hù)cookies,因此可用于登錄網(wǎng)站并保存session在請(qǐng)求之間。client可以通過Locust實(shí)例的TaskSet實(shí)例來(lái)關(guān)聯(lián),因此很容易獲取client并在任務(wù)中生成HTTP請(qǐng)求。
下面是一個(gè)生成GET請(qǐng)求到 /about 路徑的示例(在這里,我們可以假設(shè) self 是一個(gè)TaskSet 或 HttpLocust 類的實(shí)例):
response = self.client.get("/about") print "Response staus code:", response.status_code print "Response content:", response.content復(fù)制代碼下面是一個(gè)生成POST請(qǐng)求的示例:
response = self.client.post("/login", {"username": "testuser", "password": "password"})復(fù)制代碼安全模式
HTTP client被配制運(yùn)行在safe_mode。這樣做是任何請(qǐng)求在連接超時(shí)、錯(cuò)誤、相似失敗時(shí)將不會(huì)拋出異常,而是返回一個(gè)空的假Response對(duì)象。請(qǐng)求將會(huì)在Locust統(tǒng)計(jì)中算做一次失敗。返回假Response內(nèi)容屬性將會(huì)被設(shè)置為None,并且它的status_code將會(huì)是0.
手動(dòng)設(shè)置請(qǐng)求是成功或失敗
默認(rèn)情況下,請(qǐng)求被標(biāo)記為失敗除非在返回狀態(tài)碼是OK(2XX)。大部分時(shí)間內(nèi),這個(gè)默認(rèn)就是你所需要的。然而,比如在測(cè)試一個(gè)URL節(jié)點(diǎn),你期待返回狀態(tài)碼為404,或者測(cè)試一個(gè)即使錯(cuò)誤發(fā)生也會(huì)返回200的系統(tǒng),因此,需要手工控制locust來(lái)判斷是成功還是失敗。
一個(gè)可以生成失敗請(qǐng)求,即使當(dāng)響應(yīng)代碼是OK,通過使用catch_response參數(shù)和with語(yǔ)法:
with client.get("/", catch_response = True) as response:if response.content != "Success":response.failure("Got wrong response")復(fù)制代碼就像一個(gè)可以使用響應(yīng)為OK的請(qǐng)求當(dāng)做失敗來(lái)處理,一個(gè)方法就是可以使用catch_response參數(shù)和with語(yǔ)法來(lái)讓請(qǐng)求HTTP錯(cuò)誤時(shí),仍然統(tǒng)計(jì)數(shù)據(jù)為成功:
with client.get("/does_not_exist/", catch_response = True) as response:if response.status_code = 404:response.success()復(fù)制代碼使用動(dòng)態(tài)參數(shù)來(lái)分組URL請(qǐng)求
針對(duì)網(wǎng)站,有一個(gè)常用的功能是獲取URL中包含一些動(dòng)態(tài)參數(shù)的頁(yè)面數(shù)據(jù)。通常情況下,在Locust統(tǒng)計(jì)中,使用動(dòng)態(tài)分組在URL中是很有意義的。通過name參數(shù)來(lái)給HttpSession傳遞不同的請(qǐng)求方法。
比如:
# 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]")復(fù)制代碼常用庫(kù)
通常,大家想分享多個(gè)locust文件用于分享常用的庫(kù)。在這種情況下,定義項(xiàng)目根目錄用于調(diào)用Locsut是很重要的,建議將所有的locust文件有些話在項(xiàng)目的根目錄中。
一個(gè)平鋪的結(jié)構(gòu)像下面這樣:
- 項(xiàng)目根目錄
- commonlib_conf.py
- commonlib_auth.py
- locustfile_web_app.py
- locsutfile_api.py
- locustfile_ecommerce.py
locust文件可以調(diào)用常用的庫(kù)通過使用import commonlib_auth.然而,這種方法不會(huì)從locust文件中,清晰分辨出常用庫(kù)。
子文件夾可以有一個(gè)清晰的方法(查看下面的示例),但是locust僅會(huì)有運(yùn)行l(wèi)ocsut文件的位置引用相關(guān)的模塊。如果你想從你的根目錄導(dǎo)入(如,你運(yùn)行l(wèi)ocust命令的位置),確保在任何locust文件中添加常用庫(kù)前有代碼sys.path.append(os.getcwd()),會(huì)生成導(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)入常用的庫(kù):
sys.path.append(os.getcwd()) import common.auth復(fù)制代碼- 本文Locust版本0.7.5
- 原文地址:docs.locust.io/en/latest/w…
轉(zhuǎn)載于:https://juejin.im/post/59316f35a0bb9f0058bb8a7b
總結(jié)
以上是生活随笔為你收集整理的性能测试: 编写一个 Locust 文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jq 地区(省市县区)联动菜单
- 下一篇: 建议 Solr 用户更新 Apache