Locust
Locust 是比較常見的性能測驗工具,底層基于 gevent,官方介紹 它是一款易于使用、可撰寫腳本且可擴展的性能測驗工具,可以讓我們使用常規 Python 代碼定義用戶的行為,而不必陷入 UI 或限制性領域特定語言中.
Locust具有無限的可擴展性(只要提供客戶端python 代碼,適用于所有協議的性能測驗).
本文為開發性能自動化對比平臺時學習相關內容的記錄整理,
我們為什么選擇locust
| 特點 | 說明 |
|---|---|
| 開源免費 | Locust是一個開源專案,無需支付費用,可以自由使用和定制, |
| 易于學習使用 | 使用Python撰寫,學習路線平緩,擁有豐富的庫和社區支持, |
| 可擴展性靈活性高 | 可以根據需要定制測驗,以便更準確地評估應用程式的性能,第三方插件較多、 易于擴展, |
| 實時統計 | 提供實時統計功能和Web界面,方便監控和分析測驗結果, |
| 易于集成 | 可以輕松地與持續集成和持續部署工具集成,自動運行性能測驗, |
| 適用大規模的性能測驗 | 支持分布式,可以輕松地在多臺機器上運行測驗,以模擬大量用戶,這使得它非常適合進行大規模的性能測驗, |
Locust的核心部件
Master節點
負責協調和管理整個測驗程序,包括啟動和停止測驗、分發任務、收集和匯總測驗結果等,
Worker節點
實際執行測驗任務的節點,根據Master節點分配的任務進行模擬用戶行為,
Web UI
提供可視化的測驗界面,方便用戶查看測驗結果、監控測驗進度等,
測驗腳本(Load Test Script)
測驗腳本,定義模擬用戶行為的邏輯和引數,由Worker節點執行,
Locust內部運行呼叫鏈路
時序圖如下:
點擊查看時序圖說明
- 在測驗啟動時,Runner 類的 start() 方法會被呼叫,該方法會依次呼叫 EventHook 類的 fire() 方法,觸發測驗開始事件,
- Runner 類會根據配置創建 Environment 類的實體,并將其作為引數傳遞給 User 類和 TaskSet 類的建構式,同時將 User 類和 TaskSet 類添加到 Environment 類的 user_classes 屬性中,
- 在測驗運行期間,Runner 類會啟動多個用戶行程,每個用戶行程都會創建一個 User 類的實體,并呼叫 User 類的 run() 方法,該方法會呼叫 TaskSet 類的 run() 方法,從而執行用戶的任務,
- 在任務執行期間,User 類和 TaskSet 類會使用 Environment 類的 client 屬性來發送請求,并使用 Environment 類的 stats 屬性來記錄統計資訊,
- 在任務執行完成后,TaskSet 類的 run() 方法會回傳,User 類的 run() 方法會進入等待狀態,等待其他用戶完成任務,
- 在測驗結束時,Runner 類的 stop() 方法會被呼叫,該方法會依次呼叫 EventHook 類的 fire() 方法,觸發測驗結束事件,
注:fire() 方法是 Locust 中的 EventHook 類中的一個方法,用于觸發事件,在 Locust 的測驗生命周期中,有多個事件可以被觸發,例如測驗開始、測驗結束、用戶啟動、用戶完成任務等,當這些事件發生時,EventHook 類會呼叫 fire() 方法,將事件傳遞給所有注冊了該事件的回呼函式,
locust 實踐
locust安裝
| 步驟 | 說明 |
|---|---|
| 點擊跳轉安裝 Python3.7+ | 新版本標注需要Python3.7 or later |
pip install locust |
安裝 locust |
執行locust |
檢查是否安裝成功 |
$ locust -V
locust 2.15.1 from /Users/bingohe/Hebinz/venvnew/lib/python3.9/site-packages/locust (python 3.9.17)
入門示例
使用 locust 撰寫用例時,約定大于配置:
test_xxx (一般測驗框架約定)
dockerfile (docker約定)
locustfile.py (locust約定)
# locustfile.py
from locust import HttpUser, task
class HelloWorldUser(HttpUser): # 父類是個User,表示要生成進行負載測驗的系統的 HTTP“用戶”,每個user相當于一個協程鏈接 ,進行相關系統互動操作
@task # 裝飾器來標記為一個測驗任務, 表示用戶要進行的操作:訪問首頁 → 登錄 → 增、刪改查
def hello_world(self):
wait_time = between(1, 5)
# self.client發送 HTTP 請求,模擬用戶的操作
self.client.get("/helloworld")
啟動測驗
GUI 模式啟動 locust
在有locustfile.py檔案的目錄直接執行locust命令,然后訪問:http://0.0.0.0:8089/ 即可看到下面的界面:
$ locust
[2023-07-06 16:15:16,868] MacBook-Pro.local/INFO/locust.main: Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces)
[2023-07-06 16:15:16,876] MacBook-Pro.local/INFO/locust.main: Starting Locust 2.15.1

指標詳解:
- Number of users 模擬用戶數,默認 1
- Spawn rate : 生產數 (每秒)、 =>jmeter : Ramp-Up Period (in seconds), 默認 1
- Host (e.g. http://www.example.com) => 測驗目標 svr 的 絕對地址
填寫 host點擊 start 之后就會對被測服務如http://{host}/helloworld 發起請求,請求統計資料如下:

WebUI Tab說明:
| Tab名稱 | 功能描述 |
|---|---|
| New test | 點擊該按鈕可對模擬的總虛擬用戶數和每秒啟動的虛擬用戶數進行編輯 |
| Statistics | 類似于 JMeter 中 Listen 的聚合報告 |
| Charts | 測驗結果變化趨勢的曲線展示圖,包括每秒完成的請求數(RPS)、回應時間、不同時間的虛擬用戶數 |
| Failures | 失敗請求的展示界面 |
| Exceptions | 例外請求的展示界面 |
| Download Data | 測驗資料下載模塊,提供三種型別的 CSV 格式的下載,分別是:Statistics、responsetime、exceptions |
需要說明的是webui 模式有很多限制,主要用于除錯,下面將要介紹的命令列模式更為常用,
命令列模式啟動 locust
locust -f locustfile.py --headless -u 500 -r 10 --host http://www.example.com -t 1000s
? 框架是通過命令locust運行的,常用引數有:
| 引數 | 含義 |
|---|---|
-f 或 --locustfile |
指定測驗腳本檔案的路徑 |
--headless |
以非 GUI 模式運行測驗 |
-u 或 --users |
指定并發用戶數 |
-r 或 --spawn-rate |
指定用戶生成速率(即每秒生成的用戶數) |
-t 或 --run-time |
指定測驗運行的最大時間 (單位:秒),與--no-web一起使用 |
--csv |
將測驗結果輸出到 CSV 檔案中 |
--html |
將測驗結果輸出為 HTML 報告 |
--host或者 -H |
指定被測服務的地址 |
-L |
日志級別,默認為INFO |
檢查點(斷言)
Locust默認情況下會根據HTTP狀態碼來判斷請求是否成功,對于HTTP狀態碼范圍在200-399之間的回應,Locust會將其視為成功,對于HTTP狀態碼在400-599之間的回應,Locust會將其視為失敗,
如果需要根據回應內容或其他條件來判斷請求是否成功,需要手動設定檢查點:
- 使用self.client提供的catch_response=True`引數, 添加locust提供的ResponseContextManager類的背景關系方法手動設定檢查點,
- ResponseContextManager里面的有兩個方法來宣告成功和失敗,分別是
success和failure,其中failure方法需要我們傳入一個引數,內容就是失敗的原因,
from locust import HttpUser, task, between
class MyUser(HttpUser):
# 思考時間:模擬真實用戶在瀏覽應用程式時的行為
wait_time = between(1, 5)
@task
def my_task(self):
# 基于Locust提供的ResponseContextManager背景關系管理器,使用catch_response=True 引數來捕獲回應,手動標記成功或失敗,
with self.client.get("/some_page", catch_response=True) as response:
# 檢查狀態碼是否為200且回應中包含 "some_text"
if response.status_code == 200 and "some_text" in response.text:
# 如果滿足條件,標記回應為成功
response.success()
else:
# 如果條件不滿足,根據具體情況生成錯誤資訊
error_message = "Unexpected status code: " + str(response.status_code) if response.status_code != 200 else "Expected text not found in the response"
# 標記回應為失敗,并報告錯誤資訊
response.failure(error_message)
權重比例
如果需要請求有不同的比例,在Locust中,可以通過在@task裝飾器中設定weight引數為任務分配權重來實作,權重越高,任務被執行的頻率就越高,
from locust import HttpUser, task, between
class MyUser(HttpUser):
wait_time = between(1, 5)
# 設定權重為3,這個任務將被執行的頻率更高
@task(3)
def high_frequency_task(self):
self.client.get("/high_frequency_page")
# 設定權重為1,這個任務將被執行的頻率較低
@task(1)
def low_frequency_task(self):
self.client.get("/low_frequency_page")
在這個示例中,我們為high_frequency_task任務設定了權重為3,而為low_frequency_task任務設定了權重為1,這意味著在模擬用戶執行任務時,high_frequency_task任務被執行的頻率將是low_frequency_task任務的3倍,通過設定權重,我們可以根據實際需求調整不同任務在性能測驗中的執行頻率,
點擊查看在Locust內部權重的實作原理
在Locust內部,權重是通過一個名為TaskSet的類來實作的,TaskSet類包含一個名為tasks的串列,該串列包含所有定義的任務,每個任務在串列中出現的次數等于其權重,當Locust選擇要執行的任務時,它會從tasks串列中隨機選擇一個任務,這樣權重較高的任務就有更高的概率被選中,
以下是一個簡化的TaskSet類示例,以幫助理解權重是如何在Locust內部實作的:
import random
class TaskSet:
def __init__(self):
self.tasks = []
def add_task(self, task, weight=1):
for _ in range(weight):
self.tasks.append(task)
def get_random_task(self):
return random.choice(self.tasks)
task_set = TaskSet()
task_set.add_task("high_frequency_task", weight=3)
task_set.add_task("low_frequency_task", weight=1)
# 當我們呼叫 get_random_task() 方法時,權重較高的任務有更高的概率被選中
random_task = task_set.get_random_task()
在這個示例中,我們創建了一個簡化的TaskSet類,它包含一個tasks串列和兩個方法:add_task()用于添加任務及其權重,get_random_task()用于隨機選擇一個任務,權重較高的任務在tasks串列中出現的次數更多,因此它們更有可能被get_random_task()方法選中,
在實際的Locust實作中,這個概念稍微復雜一些,但基本原理是相同的:通過權重調整任務在內部串列中出現的次數,從而影響任務被選中的概率,
引數化
在現實世界中,用戶的行為通常是多樣化的,他們可能使用不同的設備、作業系統、網路條件等,為了更好地模擬這些場景,我們需要在測驗中使用不同的引數,
在性能測驗中,引數化是一種非常重要的技術手段,它允許我們使用不同的資料集運行相同的測驗場景,從而更好地模擬真實世界的用戶行為,常用的引數化方法有兩種,
使用 Locust 的內置引數化功能
from locust import HttpUser, task, between
from locust.randoms import random_string, random_number
class MyUser(HttpUser):
wait_time = between(1, 5)
@task
def random_data(self):
random_str = random_string(10)
random_num = random_number(0, 100)
self.client.post("/random", json={"text": random_str, "number": random_num})
從外部檔案讀取引數
以已經配置成白名單的鑒權 session 為例:
import csv
from locust import HttpUser, task
class CSVReader:
def __init__(self, file, **kwargs):
try:
file = open(file)
except TypeError:
pass
self.file = file
self.reader = csv.reader(file, **kwargs) # iterator
def __next__(self):
try:
return next(self.reader)
except StopIteration:
# 如果沒有下一行,則從頭開始讀
self.file.seek(0, 0)
return next(self.reader)
session = CSVReader("session.csv")
class MyUser(HttpUser):
@task
def index(self):
customer = next(ssn_reader)
self.client.get(f"/pay?session={customer[0]}")
Tag
在Locust中,標簽(Tag)是用于對任務進行分類和篩選的一種方法,通過給任務添加標簽,可以在運行Locust時只執行具有特定標簽的任務,這在執行特定場景的性能測驗或組織大量任務時非常有用,
使用場景
有時候我們會在同一個檔案中寫多個測驗場景,但是運行的時候只想運行其中一部分,即當一個測驗檔案中的task不止一個時,我們可以通過@tag給task打標簽進行分類,在執行測驗時,通過--tags name執行指定帶標簽的task,
以下是一個使用標簽的示例:
from locust import HttpUser, task, between, tag
class MyUser(HttpUser):
wait_time = between(1, 5)
# 給任務添加一個名為 "login" 的標簽
@tag("login")
@task
def login_task(self):
self.client.post("/login", json={"username": "user", "password": "pass"})
# 給任務添加一個名為 "profile" 的標簽
@tag("profile")
@task
def profile_task(self):
self.client.get("/profile")
# 給任務添加兩個標簽:"shopping" 和 "checkout"
@tag("shopping", "checkout")
@task
def checkout_task(self):
self.client.post("/checkout")
在這個示例中,我們為三個任務分別添加了不同的標簽,login_task任務具有"login"標簽,profile_task任務具有"profile"標簽,而checkout_task任務具有"shopping"和"checkout"兩個標簽,
運行Locust時,可以通過使用--tags和--exclude-tags選項來指定要執行或排除的標簽,例如,要僅執行具有"login"標簽的任務,可以運行:
locust --tags login
要排除具有"shopping"標簽的任務,可以運行:
locust --exclude-tags shopping
這樣,我們就可以根據需要執行特定場景的性能測驗,而不需要修改代碼,
點擊查看如何在Locust屬性中指定 tag
在Locust中,可以使用tags屬性來在HttpUser子類中指定標簽,以下是一個示例:
from locust import HttpUser, task, between, tag
@tag("login")
class LoginTasks(HttpUser):
wait_time = between(1, 5)
@task
def login_task(self):
self.client.post("/login", json={"username": "user", "password": "pass"})
@tag("profile")
class ProfileTasks(HttpUser):
wait_time = between(1, 5)
@task
def profile_task(self):
self.client.get("/profile")
@tag("shopping", "checkout")
class CheckoutTasks(HttpUser):
wait_time = between(1, 5)
@task
def checkout_task(self):
self.client.post("/checkout")
在這個示例中,我們創建了三個不同的HttpUser子類,分別為LoginTasks、ProfileTasks和CheckoutTasks,我們在類級別使用@tag()裝飾器為每個子類添加了標簽,LoginTasks具有"login"標簽,ProfileTasks具有"profile"標簽,而CheckoutTasks具有"shopping"和"checkout"兩個標簽,
與之前的示例類似,可以使用--tags和--exclude-tags選項來指定要執行或排除的標簽,在這種情況下,標簽將應用于整個HttpUser子類,而不僅僅是單個任務,
集合點
什么是集合點?
集合點用以同步虛擬用戶,以便恰好在同一時刻執行任務,在[測驗計劃]中,可能會要求系統能夠承受1000 人同時提交資料,可以通過在提交資料操作前面加入集合點,這樣當虛擬用戶運行到提交資料的集合點時,就檢查同時有多少用戶運行到集合點,如果不到1000 人,已經到集合點的用戶在此等待,當在集合點等待的用戶達到1000 人時,1000 人同時去提交資料,從而達到測驗計劃中的需求,
注意:Locust框架本身沒有直接封裝集合點的概念 ,需要間接通過gevent并發機制,使用gevent的鎖來實作,
在 Locust 中 實作集合點前,我們先了解兩個概念:
gevent中的Semaphore信號量locust中的事件鉤子all_locusts_spawned
Semaphore
信號量(Semaphore)是一種用于控制對共享資源訪問的同步原語,它是計算機科學和并發編程中的一個重要概念,最早由著名計算機科學家Edsger Dijkstra于1960s提出,信號量用于解決多執行緒或多行程環境中的臨界區問題,以防止對共享資源的競爭訪問,
點擊查看`Semaphore`實作原理
信號量的作業原理是通過維護一個計數器來表示可用資源的數量,當一個執行緒或行程想要訪問共享資源時,它需要請求信號量,信號量會檢查其計數器值:
如果計數器值大于0,表示有可用資源,信號量會減少計數器值并允許執行緒或行程訪問共享資源,
如果計數器值等于0,表示沒有可用資源,信號量會阻塞執行緒或行程,直到其他執行緒或行程釋放資源,
當執行緒或行程完成對共享資源的訪問后,它需要釋放信號量,此時,信號量會增加計數器值,表示資源已釋放并可供其他執行緒或行程使用,
信號量通常有兩種型別:
二進制信號量(Binary Semaphore):計數器值只能為0或1,二進制信號量通常用于實作互斥鎖(Mutex),以確保一次只有一個執行緒或行程訪問共享資源,
計數信號量(Counting Semaphore):計數器值可以為任意正整數,計數信號量用于限制對共享資源的并發訪問數量,以實作有限的資源池,
許多編程語言和庫都提供了信號量的實作,例如Python中的threading.Semaphore和gevent.lock.Semaphore,使用信號量可以幫助解決并發編程中的同步和資源競爭問題,
gevent.lock.Semaphore是gevent庫中提供的信號量實作,gevent是一個基于協程的Python并發庫,使用輕量級的綠色執行緒(greenlet)提供高性能的并發,gevent.lock.Semaphore允許您在gevent協程中同步對共享資源的訪問,
以下是gevent.lock.Semaphore的主要特點和使用方法:
- 初始化:要創建一個信號量,您可以實體化
gevent.lock.Semaphore類,在初始化時,可以選擇設定信號量的初始值(默認值為1),
from gevent.lock import Semaphore
# 創建一個具有默認初始值(1)的信號量
sem = Semaphore()
# 創建一個具有自定義初始值(5)的信號量
sem_with_initial_value = https://www.cnblogs.com/Detector/p/Semaphore(value=5)
- 請求資源(acquire):當協程需要訪問共享資源時,它應該呼叫
Semaphore.acquire()方法,如果信號量的計數器值大于0,acquire()方法將減少計數器值并立即回傳,如果計數器值為0,acquire()方法將阻塞協程,直到其他協程釋放資源,
sem.acquire()
# 在此處訪問共享資源
- 釋放資源(release):當協程完成對共享資源的訪問后,它應該呼叫
Semaphore.release()方法,這將增加信號量的計數器值,表示資源已釋放并可供其他協程使用,
# 完成對共享資源的訪問
sem.release()
- 使用背景關系管理器:
gevent.lock.Semaphore還可以作為背景關系管理器使用,以確保在訪問共享資源的代碼塊結束時自動釋放信號量,這可以簡化代碼并防止忘記釋放信號量,
with sem:
# 在此處訪問共享資源
# 信號量會在這里自動釋放
總之,gevent.lock.Semaphore是gevent庫中提供的信號量實作,用于在協程之間同步對共享資源的訪問,通過使用Semaphore.acquire()和Semaphore.release()方法,您可以確保在gevent協程中正確處理并發訪問,
all_locusts_spawned 事件
在 Locust 中,事件是一個非常重要的概念,事件允許我們在 Locust 的生命周期中的特定時刻執行自定義的操作,通過監聽和處理這些事件,我們可以擴展 Locust 的功能,以滿足測驗需求,
spawning_complete 是 Locust 中的一個事件,表示所有的 Locust 用戶(user)已經生成完成,當 Locust 開始運行測驗并生成用戶時,它會逐漸創建用戶實體,一旦所有的用戶都被創建,spawning_complete 事件就會被觸發,你可以在這個事件中執行一些特定的操作,例如輸出日志訊息、收集統計資訊或執行其他自定義操作,
要監聽 spawning_complete 事件,你可以使用 locust.events.spawning_complete 事件鉤子,例如:
from locust import events
@events.spawning_complete.add_listener
def on_spawning_complete():
print("All users have been spawned!")
在這個示例中,當所有的用戶生成完成時,我們會輸出一條訊息 "All users have been spawned!",你可以根據需要替換為其他操作,
點擊查看 `Locust` 生命周期中其他的事件
sequenceDiagram participant S as Script participant L as Locust Note over S, L: Test Starts S->>L: test_start L->>S: on_test_start Note over S, L: Spawning Users S->>L: spawning_start L->>S: on_spawning_start loop for each user S->>L: user_add L->>S: on_user_add end S->>L: spawning_complete L->>S: on_spawning_complete Note over S, L: Running Test loop for each request S->>L: request L->>S: on_request end Note over S, L: Test Stops S->>L: test_stop L->>S: on_test_stoptest_start:測驗開始時觸發,spawning_start:生成用戶時觸發,user_add:每個用戶被添加時觸發,spawning_complete:所有用戶生成完成時觸發,request:每個請求發生時觸發,test_stop:測驗停止時觸發,
了解完上面兩個概念,接下來我們只需要兩步走:
- 在腳本啟動時,使用all_locust_spawned.acquire() 阻塞行程
- 撰寫一個函式,在 用戶全部創建完成時觸發 all_locust_spawned.release()
示例代碼:
from locust import HttpUser, task, between
from gevent.lock import Semaphore
from locust import events
all_locust_spawned = Semaphore()
all_locust_spawned.acquire() # 阻塞
class MyUser(HttpUser):
wait_time = between(1, 1)
def on_start(self):
global all_locust_spawned
all_locust_spawned.wait(3) # 同步鎖等待時間
@task
def task_rendezvous(self):
self.client.get("/rendezvous")
# 添加集合點事件處理器
@events.spawning_complete.add_listener # 所有的Locust實體產生完成時觸發
def on_spawning_complete(**_kwargs):
global all_locust_spawned
all_locust_spawned.release()
分布式
當我們需要大量的并發用戶,而單個計算機可能無法生成足夠的負載來模擬這種情況時,分布式壓力測驗可以解決這個問題,我們可以通過將壓力測驗分布到多個計算機上來生成更大的負載,并更準確地評估系統的性能,
Locust 的限制
Locust 使用 Python 的 asyncio 庫來實作異步 I/O,這意味著它可以充分利用多核 CPU 的性能,然而,由于 Python 的全域解釋器鎖(GIL)限制,單個 Python 行程無法充分利用多核 CPU,
為了解決這個問題,Locust 支持在單個計算機上運行多個從節點(worker node),這樣可以充分利用多核 CPU 的性能,
當在單臺計算機上運行多個從節點時,每個從節點將運行在一個單獨的行程中,從而避免了 GIL 的限制,這樣,我們可以充分利用多核 CPU 的性能,生成更大的負載,
單機主從模式
注意: slave 的節點數要小于等于本機的處理器數
在單機主從模式下,主節點和從節點都運行在同一臺計算機上,這種模式適用于在本地開發環境中進行壓力測驗,或者在具有多核 CPU 的單臺服務器上進行壓力測驗,以下是在單機主從模式下實作分布式壓力測驗的步驟:
-
安裝 Locust:在計算機上安裝 Locust,使用
pip install locust命令進行安裝, -
撰寫 Locust 測驗腳本:撰寫一個 Locust 測驗腳本,這個腳本將在主節點和從節點上運行,將此腳本保存為
locustfile.py, -
啟動主節點:在計算機上運行
locust --master命令啟動主節點,監聽默認埠(8089), -
啟動從節點:在計算機上運行
locust --worker --master-host 127.0.0.1命令啟動一個從節點,根據需要,可以啟動多個從節點, -
運行分布式壓力測驗:訪問 Locust 的 Web 界面(http://127.0.0.1:8089),開始測驗,
單機模式下,如何讓每個從節點都運行在不同的 CPU 上
點擊查看如何單機模式下,每個從節點都運行在不同的 CPU 上
在單機主從模式下,確保啟動的多個從節點運行在不同的 CPU 核心上,可以通過為每個從節點設定 taskset 命令來實作,taskset 是一個 Linux 命令,可以用來設定行程的 CPU 親和性,即將行程系結到特定的 CPU 核心上運行,
以下是在單機主從模式下,確保啟動的多個從節點運行在不同 CPU 核心上的步驟:
-
啟動主節點:在計算機上運行
locust --master命令啟動主節點,監聽默認埠(8089), -
啟動從節點:在計算機上運行以下命令啟動從節點,并將其系結到特定的 CPU 核心上:
taskset -c CORE_NUMBER locust --worker --master-host 127.0.0.1
其中,CORE_NUMBER 是要將從節點系結到的 CPU 核心編號(從 0 開始),例如,要將從節點系結到第一個 CPU 核心上,可以運行以下命令:
taskset -c 0 locust --worker --master-host 127.0.0.1
- 根據需要,可以啟動多個從節點,并將它們分別系結到不同的 CPU 核心上,例如,要將第二個從節點系結到第二個 CPU 核心上,可以運行以下命令:
taskset -c 1 locust --worker --master-host 127.0.0.1
請注意,taskset 命令僅適用于 Linux 系統,在 Windows 或 macOS 上,可以嘗試使用類似的工具,例如 Windows 上的 start /affinity 命令或 macOS 上的 cpulimit 工具,
通過使用 taskset 命令或類似的工具,我們可以確保在單機主從模式下,啟動的多個從節點運行在不同的 CPU 核心上,這有助于充分利用多核 CPU 的性能,生成更大的負載,
多機主從模式
操作與單機模式基本一樣,訪問 Locust 的 Web 界面時訪問的時主節點的地址(http://MASTER_IP_ADDRESS:8089),
因為主節點和從節點之間通過網路通信,因此,在選擇主節點和從節點的計算機時,需要確保它們之間的網路連接暢通,此外,為了獲得準確的測驗結果,務必確保主節點和從節點之間的網路延遲較低,
分布式模式下的命令引數
| 命令引數 | 說明 |
|---|---|
--master |
將當前 Locust 實體作為主節點(master node)運行, |
--worker |
將當前 Locust 實體作為從節點(worker node)運行, |
--master-host |
指定主節點的 IP 地址或主機名,默認值為 127.0.0.1, |
--master-port |
指定主節點的埠號,默認值為 5557, |
--master-bind-host |
指定主節點系結的 IP 地址或主機名,默認值為 *(所有介面), |
--master-bind-port |
指定主節點系結的埠號,默認值為 5557, |
--expect-workers |
指定主節點期望連接的從節點數量,默認值為 1, |
--expect-workers 引數用于指定主節點期望連接的從節點數量,如果實際連接的從節點數量沒有達到這個值,主節點會繼續等待,直到足夠的從節點連接上來,
在實際運行分布式壓力測驗時,主節點會在 Web 界面上顯示連接的從節點數量,如果實際連接的從節點數量沒有達到 --expect-workers 指定的值,你可以在 Web 界面上看到一個警告訊息,提示你主節點正在等待更多從節點的連接,
docker 運行locust
使用 容器的方式運行 locust 的優勢和缺點都非常明顯:
| 優勢 | 描述 |
|---|---|
| 環境一致性 | Docker 可以確保在不同計算機上運行的 Locust 環境是一致的, |
| 便于部署 | 使用 Docker 可以簡化 Locust 的部署程序, |
| 易于擴展 | Docker 可以與容器編排工具結合使用,實作 Locust 從節點的自動擴展, |
| 隔離性 | Docker 容器提供了一定程度的隔離性,將 Locust 運行環境與宿主機系統隔離, |
| 缺點 | 描述 |
|---|---|
| 性能開銷 | Docker 容器可能存在一定程度的性能損失,與在宿主機上直接運行 Locust 相比, |
| 學習曲線 | 對于不熟悉 Docker 的用戶,可能需要一定時間學習 Docker 的基本概念和使用方法, |
| 系統資源占用 | 運行 Docker 容器需要消耗一定的系統資源(如 CPU、記憶體、磁盤空間等), |
但是以下這些場景使用 Docker 來運行 Locust 是一個更好的選擇:
-
分布式壓力測驗:在分布式壓力測驗中,需要在多臺計算機上運行 Locust 主節點和從節點,使用 Docker 可以確保所有節點的運行環境一致,簡化部署程序,
-
云環境部署:如果你需要在云環境(如 AWS、Azure、GCP 等)中進行壓力測驗,使用 Docker 可以簡化部署程序,并充分利用云平臺提供的容器服務(如 Amazon ECS、Google Kubernetes Engine 等),
-
CI/CD 集成:如果你需要將壓力測驗集成到持續集成/持續部署(CI/CD)流程中,使用 Docker 可以簡化集成程序,許多 CI/CD 工具(如 Jenkins、GitLab CI、Travis CI 等)都支持 Docker 集成,
-
避免環境沖突:如果你的開發或測驗環境中已經安裝了其他 Python 應用程式,可能會出現依賴項沖突,使用 Docker 可以將 Locust 運行環境與宿主機系統隔離,避免潛在的環境沖突,
-
團隊協作:在團隊協作程序中,使用 Docker 可以確保每個團隊成員都使用相同的 Locust 運行環境,從而避免因環境差異導致的問題,
具體使用步驟
-
首先,確保你已經安裝了 Docker,如果尚未安裝,請參考 Docker 官方檔案 以獲取適用于你的作業系統的安裝說明,
-
撰寫一個 Locust 測驗腳本,例如,創建一個名為
locustfile.py的檔案,內容如下:
from locust import HttpUser, task, between
class MyUser(HttpUser):
wait_time = between(1, 5)
@task
def my_task(self):
self.client.get("/")
- 使用以下命令從 Docker Hub 拉取官方的 Locust 鏡像:
docker pull locustio/locust
- 使用以下命令在 Docker 中運行 Locust,
docker run --rm -p 8089:8089 -v $PWD:/mnt/locust locustio/locust -f /mnt/locust/locustfile.py --host TARGET_HOST
在這個命令中,我們將當前目錄(包含 locustfile.py 檔案)掛載到 Docker 容器的 /mnt/locust 目錄,然后,我們使用 -f 引數指定要運行的 Locust 測驗腳本,并使用 --host 引數指定目標主機地址,
- 訪問 Locust 的 Web 界面,在瀏覽器中打開
http://localhost:8089,你將看到 Locust 的 Web 界面,在這里,你可以開始壓力測驗并查看結果,
通過以上步驟,你可以在 Docker 中運行 Locust,無需在本地環境中安裝 Locust,
總之,在需要確保環境一致性、簡化部署程序、集成到 CI/CD 流程、避免環境沖突或團隊協作的場景下,使用 Docker 來運行 Locust 是一個很好的選擇,通過使用 Docker,你可以輕松地在不同的計算機或云環境中運行壓力測驗,從而實作更大規模的分布式壓力測驗,
高性能 FastHttpUser
Locust 的默認 HTTP 客戶端使用http.client,如果計劃以非常高的吞吐量運行測驗并且運行 Locust 的硬體有限,那么它有時效率不夠,
FastHttpUser 是 Locust 提供的一個特殊的用戶類,用于執行 HTTP 請求,與默認的 HttpUser 不同,FastHttpUser 使用 C 語言庫 gatling 撰寫的 httpclient 進行 HTTP 請求, 有時將給定硬體上每秒的最大請求數增加了 5 到 6 倍,在相同的并發條件下使用FastHttpUser能有效減少對負載機的資源消耗從而達到更大的http請求,
優勢
-
性能:
FastHttpUser的主要優勢是性能,由于它使用 C 語言庫進行 HTTP 請求,它的性能通常比默認的HttpUser更高,這意味著在相同的硬體資源下,你可以使用FastHttpUser生成更大的負載, -
資源占用:與默認的
HttpUser相比,FastHttpUser通常具有較低的資源占用(如 CPU、記憶體等),這意味著在進行壓力測驗時,你可以在同一臺計算機上運行更多的并發用戶, -
更高的并發能力:由于
FastHttpUser的性能和資源占用優勢,它可以更好地支持大量并發用戶的壓力測驗,這對于需要模擬大量并發用戶的場景(如高流量 Web 應用程式、API 等)非常有用,
然而需要注意的是FastHttpUser 也有一些局限性,例如,它可能不支持某些特定的 HTTP 功能(如自定義 SSL 證書、代理設定等),在選擇使用 FastHttpUser 時,需要權衡性能優勢和功能支持,如果測驗場景不需要大量并發用戶,或者需要特定的 HTTP 功能,使用默認的 HttpUser 可能更合適,
以下是一個使用 FastHttpUser 的 Locust 測驗腳本示例:
from locust import FastHttpUser, task, between
class MyFastHttpUser(FastHttpUser):
wait_time = between(1, 5)
@task
def my_task(self):
self.client.get("/")
測驗gRPC等其他協議
locust 并非 http 介面測驗工具 , 只是內置了 “HttpUser” 示例 ,理論上來說,只要提供客戶端,它可以測驗任何協議,
如果有測驗 gRPC、XML-RPC、requests-based libraries/SDKs等需求,可以參考:
https://docs.locust.io/en/stable/testing-other-systems.html
其他
主流性能測驗工具對比
下面是 Locust、JMeter、Wrk 和 LoadRunner 四款性能測驗工具的優缺點和支持的功能的對比表格:
| 工具名稱 | 優點 | 缺點 | 支持的功能 |
|---|---|---|---|
| Locust | - 簡單易用,支持 Python 語言 - 可以在代碼中撰寫測驗場景,靈活性高 - 可以使用分布式部署,支持大規模測驗 - 支持 Web 和 WebSocket 測驗 |
- 功能相對較少,不支持 GUI - 對于非 Python 開發人員不太友好 - 在大規模測驗時需要手動管理分布式節點 |
- HTTP(S)、WebSocket 測驗 - 支持斷言、引數化、資料驅動等功能 - 支持分布式測驗 |
| JMeter | - 功能豐富,支持多種協議 - 支持 GUI,易于使用 - 支持分布式部署,支持大規模測驗 - 支持插件擴展,可以擴展功能 |
- 性能較差,不適合高并發測驗 - 記憶體占用較高,需要較大的記憶體 - 學習曲線較陡峭 |
- HTTP(S)、FTP、JDBC、JMS、LDAP、SMTP、TCP、UDP 等多種協議的測驗 - 支持斷言、引數化、資料驅動等功能 - 支持分布式測驗 |
| Wrk | - 性能優異,支持高并發測驗 - 支持 Lua 腳本撰寫,靈活性高 - 支持多種輸出格式,方便結果分析 |
- 功能相對較少,不支持 GUI - 只支持 HTTP 協議測驗 - 學習曲線較陡峭 |
- HTTP(S) 測驗 - 支持斷言、引數化、資料驅動等功能 |
| LoadRunner | - 功能豐富,支持多種協議 - 支持 GUI,易于使用 - 支持分布式部署,支持大規模測驗 - 支持插件擴展,可以擴展功能 |
- 價格較高,不適合小型團隊使用 - 學習曲線較陡峭 - 對于非 Windows 平臺的支持不夠友好 |
- HTTP(S)、FTP、JDBC、JMS、LDAP、SMTP、TCP、UDP 等多種協議的測驗 - 支持斷言、引數化、資料驅動等功能 - 支持分布式測驗 |
需要注意的是,這些工具的優缺點和支持的功能只是相對而言的,具體使用時需要根據實際需求和場景選擇,
文中可能存在描述不正確,歡迎大神們指正補充! 感謝閱讀,如果覺得對你有幫助,就在右下角點個贊吧,感謝!合抱之木,生于毫末;九層之臺,起于累土;千里之行,始于足下,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/556849.html
標籤:其他
上一篇:AI重塑千行百業,華為云發布盤古大模型3.0和昇騰AI云服務
下一篇:返回列表
