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