文章略長Locust學習筆記彙總
若有錯誤,請指正
内容以官網為準https://locust.io/
Locust簡介 Locust是什麼?
Locust是一個簡單易用的分布式使用者負載測試工具。它用于web站點(或其他系統)的負載測試,并計算一個系統可以處理多少并發使用者。
在測試中,一群locusts(蝗蟲)會攻擊你的網站。每個locusts (或者測試使用者)的行為由您定義,叢集過程由web UI實時監控。這将幫助你在允許實際使用者使用之前測試并确定代碼中的瓶頸。
Locust完全基于事件,是以可以在一台計算機上支援數千個并發使用者。與許多其他基于事件的應用程式不同,它不使用回調。相反,它通過gevent使用輕量級程序。每個聚集站點上的蝗蟲實際上是在它自己的程序中運作的(正确的說是greenlet)。這允許你使用Python編寫非常有表現力的場景,而不用回調使代碼複雜化。
Locust功能特性
用普通的Python編寫使用者測試場景不需要笨拙的UI或龐大的XML,隻需像通常那樣編碼即可。基于協程而不是回調,您的代碼看起來和行為都與正常的、阻塞Python代碼一樣。
分布式和可擴充——支援成千上萬的使用者Locust支援在多台機器上運作負載測試。由于基于事件,即使一個Locust節點也可以在一個程序中處理數千個使用者。這背後的部分原因是,即使你模拟了那麼多使用者,也不是所有使用者都積極的通路你的系統。通常,使用者無所事事,想知道下一步該怎麼做。每秒請求數不等于線上使用者數。
基于web的UILocust具有簡潔的HTML + JS使用者界面,可實時顯示相關的測試細節。而且由于UI是基于Web的,是以它是跨平台且易于擴充的。
可以測試任何系統盡管Locust是面向web的,但它幾乎可以用于測試任何系統。隻需要未測試的内容編寫一個用戶端,然後用Locust通路!超級簡單!
可控Locust很小,很容易被入侵,我們打算保持這種狀态。事件I / O和協程的所有繁重工作都委托給gevent。替代測試工具的脆弱性是我們建立Locust的原因。
Locust背景
Locust 的誕生是因為我們厭倦了現有的解決方案。對我來說,他們都沒有解決正确的問題,沒有抓住重點。我們已經嘗試了Apache JMeter和Tsung。這兩種工具都可以使用;我們已經在工作中多次使用了前一種方法。JMeter附帶UI界面,您可能會認為這是一件好事。但是您很快就會意識到,通過某些點選界面“編碼”你的測試方案是一種陷阱。其次,JMeter是線程綁定的。這意味着對于要模拟的每個使用者,都需要一個單獨的線程。不用說,在一台機器上對成千上萬的使用者進行基準測試是不可行的。
另一方面,Tsung沒有這些線程問題,因為它是用Erlang編寫的。它可以利用BEAM自身提供的輕量級流程,并且可以愉快地擴充規模。但是在定義測試場景時,Tsung和JMeter一樣有限。它提供了基于XML的DSL來定義使用者在測試時的行為方式。我想您可以想象“編碼”這一點的恐怖。完成後顯示各種圖形或報告,需要對測試生成的日志檔案進行後期處理。隻有這樣,您才能了解測試的具體情況。
不管怎樣,我們在創造Locust的時候已經嘗試解決這些問題。希望以上這些痛點都不存在。
我猜你可能會說我們真的隻是在這裡撓癢癢。我們希望其他人會發現它和我們一樣有用。
安裝
Locust可以在PyPI上使用,并且可以與pip一起安裝。
1$ pip3 install locust
如果您想要最新版本,可以使用pip直接從我們的Git存儲庫安裝。例如,要使用Python 3安裝master分支:
1pip3 install -e git://github.com/locustio/[email protected]#egg=locustio
安裝了Locust之後,您的shell中應該可以使用Locust指令。(如果您沒有使用virtualenv—您應該使用—請確定您的python腳本目錄位于您的路徑上)。
要檢視可用的選項,請運作:
1$ locust --help
Python版本支援
在Python 3.6、3.7和3.8
Windows上安裝locust
在Windows上,運作pip install locustio 即可。
但是,如果沒有成功,那麼可以先安裝為pyzmq、gevent和greenlet預先建構的二進制包來修複它。
你可以在這裡找到一個非官方的集合,預先建立的python包的windows:http://www.lfd.uci.edu/~gohlke/pythonlibs/
下載下傳預建構的.whl檔案後,可以使用以下方法進行安裝:
1$ pip install name-of-file.whl
然後可以使用pip install locustio進行安裝。
注意:在Windows上運作Locust應該可以很好地開發和測試您的負載測試腳本。但是,在運作大規模測試時,建議您在Linux機器上這樣做,因為gevent在Windows下的性能很差。
macOS上安裝Locust
目前是使用Homebrew在OS X上安裝gevent的最短路徑。
安裝Homebrew。
安裝libev(gevent的依賴):
1brew install libev
增加打開檔案最大數限制
機器上的每個HTTP連接配接打開一個新檔案(技術上稱為檔案描述符)。作業系統可以為可打開的檔案的最大數量設定一個較低的限制。如果該限制小于測試中模拟使用者的數量,則會發生故障。
将作業系統的預設最大檔案數限制增加到大于你要運作的模拟使用者數的數量。如何執行此操作取決于所使用的作業系統。
快速入門Locust
例子locustfile.py下面是一個簡單的locustfile.py的小例子:
1from locust import HttpLocust, TaskSet, between23def login(l):4 l.client.post("/login", {"username":"ellen_key", "password":"education"})56def logout(l):7 l.client.post("/logout", {"username":"ellen_key", "password":"education"})89def index(l):10 l.client.get("/")1112def profile(l):13 l.client.get("/profile")1415class UserBehavior(TaskSet):16 tasks = {index: 2, profile: 1}1718 def on_start(self):19 login(self)2021 def on_stop(self):22 logout(self)2324class WebsiteUser(HttpLocust):25 task_set = UserBehavior26 wait_time = between(5.0, 9.0)
這裡我們定義了一些Locust任務,它們是普通的Python可調用函數,帶有一個參數(一個Locust類執行個體)。這些任務收集在tasks屬性中的TaskSet類下。然後,我們有一個代表使用者的HttpLocust類,在這個類中定義了模拟使用者在執行任務之間應等待的時間,以及哪個TaskSet類應定義使用者的“行為”。TaskSet類可以嵌套。
HttpLocust類繼承自Locust類,并且添加了一個client屬性,該屬性是HttpSession的執行個體,可用于發出HTTP請求。
預設情況下,我們不再設定代理以提高性能。如果确實想要測試請求通過HTTP代理,你可以繼承HttpLocust類,并将trust_env字段設定為True。有關更多細節,請參閱請求的文檔。
我們聲明任務的另一種方法(通常更友善)是使用@task裝飾器。下面代碼與上面的代碼效果一樣:
1from locust import HttpLocust, TaskSet, task, between23class UserBehaviour(TaskSet):4 def on_start(self):5 """ on_start is called when a Locust start before any task is scheduled """6 self.login()78 def on_stop(self):9 """ on_stop is called when the TaskSet is stopping """10 self.logout()1112 def login(self):13 self.client.post("/login", {"username":"ellen_key", "password":"education"})1415 def logout(self):16 self.client.post("/logout", {"username":"ellen_key", "password":"education"})1718 @task(2)19 def index(self):20 self.client.get("/")2122 @task(1)23 def profile(self):24 self.client.get("/profile")2526class WebsiteUser(HttpLocust):27 task_set = UserBehaviour28 wait_time = between(5, 9)
Locust類(以及HttpLocust,因為它是一個子類)還允許您指定任務執行之間的等待時間(wait_time = between(5, 9))以及其他使用者行為。使用between函數,可以在指定的最大值和最小值之間随機選擇時間,但是可以通過将wait_time設定為任意函數來使用任何使用者定義的時間分布。例如,對于平均時間為1秒的指數分布等待時間:
1import random23class WebsiteUser(HttpLocust):4 task_set = UserBehaviour5 wait_time = lambda self: random.expovariate(1)*1000
啟動Locust
要使用上述Locust檔案運作Locust,如果該檔案名為locustfile.py且位于目前工作目錄中,則可以運作:$ locust如果Locust檔案位于與locustfile.py在不同的子目錄/或者檔案名不一樣,則使用參數-f+檔案名:$ locust -f locust_files/my_locust_file.py
要在多個程序中運作Locust,我們可以通過指定--master:$ locust -f locust_files/my_locust_file.py --master然後我們将啟動任意數量的從屬程序:$ locust -f locust_files/my_locust_file.py --slave如果要在多台機器上運作Locust,則在啟動從屬伺服器時還必須指定主伺服器主機(在單台計算機上運作Locust時不需要,因為主伺服器主機預設為127.0.0.1):$ locust -f locust_files/my_locust_file.py --slave --master-host=192.168.0.100還可以在配置檔案(locust.conf或~/.locust.conf)或以LOCUST_字首的env vars中設定參數例如:(這将與上一個指令執行相同的操作)$ LOCUST_MASTER_HOST=192.168.0.100 locust
注意:要檢視所有可用選項,請鍵入:locust—help
打開Locust的Web界面
使用上述指令行之一啟動Locust後,應打開浏覽器并輸入http://127.0.0.1:8089或者http://localhost:8089(如果您在本地運作Locust)。然後,你會看到以下界面:(或者浏覽器中通路ipv6本地位址:http://[::1]:8089, 也可)
Locust UI
如果您在逐漸負載模式下運作Locust,,則應該使用以下Locust UI,如下所示:
Locust UI Step Load Mode
然後在上述幾面中輸入數量,以及要通路的URL,點選Start即可看到響應資料,如下圖:
性能相應資料
Locust--CSV存儲測試資料
Locust的測試結果儲存到CSV檔案,在這種情況下,有兩種方法可以執行此操作。
首先,通過Web UI運作Locust時,可以在“Download Data”頁籤下得到CSV檔案。
其次,可以使用标簽運作Locust,該标簽将定期儲存兩個CSV檔案。如果計劃使用--no-web标簽以自動化方式運作Locust,這将特别有用:
1$ locust -f examples/basic.py --csv=example --no-web -t10m
檔案将被命名為example_response_times.csv 和 example_stats.csv (使用--csv=example)并記錄Locust建構的資訊。
如果你想要更快(慢)的寫入速度,也可以自動以寫入頻率:
1import locust.stats2# 預設為2秒3locust.stats.CSV_STATS_INTERVAL_SEC = 5
此資料将寫入兩個檔案,并将_response_times.csv和_stats.csv添加到你提供的名稱中:
1$ cat example_response_times.csv2"Name","# requests","50%","66%","75%","80%","90%","95%","98%","99%","99.9%","99.99%","100%"3"GET /",31,4,4,4,4,4,4,4,4,4,4,44"/does_not_exist",0,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A"5"GET /stats/requests",38,3,4,4,4,4,5,5,5,5,5,56"None Total",69,3,4,4,4,4,4,5,5,5,5,5
和
1$ cat example_stats.csv2"Type","Name","# requests","# failures","Median response time","Average response time","Min response time","Max response time","Average Content Size","Requests/s"3"GET","/",51,0,4,3,2,6,12274,0.894"GET","/does_not_exist",0,56,0,0,0,0,0,0.005"GET","/stats/requests",58,0,3,3,2,5,1214,1.016"None","Total",109,56,3,3,2,6,6389,1.89
編寫一個locustfile
locustfile是普通的python檔案。唯一的要求是至少聲明一個類(我們稱之為locust類),該類繼承自locust類。
Locust類
一個Locust類代表一個使用者(或者一個叢集Locust)。Locust将為每個正在模拟的使用者生成(孵化)一個Locust類執行個體。Locust類通常應該定義一些屬性。
task_set屬性
task_set屬性應該指向一個TaskSet類,這個類定義了使用者的行為,下面将對其進行更詳細的描述。
wait_time屬性
除了task_set屬性,還應該聲明一個wait_time方法。它用于确定模拟使用者在執行任務之間将等待多長時間。Locust提供了一些内置的函數,傳回一些常用的wait_time方法。
最常見的是 between 。它用于使模拟使用者在每次執行任務後等待介于最小值和最大值之間的随機時間。其他内置的等待時間函數是constant和constant_pacing。
使用以下locustfile,每個使用者将在任務之間等待5到15秒:
1from locust import Locust, TaskSet, task, between23class MyTaskSet(TaskSet):4 @task5 def my_task(self):6 print("executing my_task")78class User(Locust):9 task_set = MyTaskSet10 wait_time = between(5, 15)
wait_time方法應該傳回秒數(或幾分之一秒),也可以在TaskSet類上聲明,在這種情況下,它将僅用于該TaskSet。
也可以直接在Locust或TaskSet類上聲明自己的wait_time方法。接下來的Locust類将開始休眠1秒鐘,然後休眠1秒,2秒,3秒,等等。
1class MyLocust(Locust):2 task_set = MyTaskSet3 last_wait_time = 045 def wait_time(self):6 self.last_wait_time += 17 return self.last_wait_time
weight (權重)屬性
如果檔案中存在多個locust類,并且指令行中沒有指定locust,則每個新生成的locust将從現有locust中随機選擇。否則,你可以從相同的檔案中像下面這樣指定使用哪個locust:$ locust -f locust_file.py WebUserLocust MobileUserLocust
如果你想讓這些locust中的一個執行得更頻繁,你可以給這些類設定一個權重屬性。例如,Web使用者是mobile使用者的三倍:
1class WebUserLocust(Locust):2 weight = 33 ...45class MobileUserLocust(Locust):6 weight = 17 ...
host屬性
host屬性host屬性是要加載的URL字首(https://cn.bing.com);通常,是在Locust的Web UI或指令行中指定的,在啟動Locust時使用--host。如果在locust類中聲明了一個host屬性,則在指令行或Web請求中未指定--host的情況下将使用該屬性。。
TaskSet類
如果Locust類代表蝗蟲群,則可以說TaskSet類代表蝗蟲的大腦。每個Locust類必須設定一個task_set屬性,該屬性指向TaskSet。
顧名思義,TaskSet是任務的集合。這些任務是普通的python可調用對象,并且,如果我們正在對拍賣網站進行負載測試,則可以完成諸如“加載起始頁”,“搜尋某些産品”和“競标”之類的工作。
啟動負載測試時,派生的Locust類的每個執行個體将開始執行其TaskSet。接下來的情況是每個TaskSet将選擇一個任務并調用它。然後,它将等待Locust類的wait_time方法指定的秒數(除非已直接在TaskSet上聲明了wait_time方法,在這種情況下,它将使用自己的方法)。然後它将再次選擇要調用的新任務,再次等待,依此類推。
聲明任務
TaskSet聲明任務的典型方式是使用task裝飾器。
這裡有一個例子:
1from locust import Locust, TaskSet, task23class MyTaskSet(TaskSet):4 @task5 def my_task(self):6 print("Locust instance (%r) executing my_task" % (self.locust))78class MyLocust(Locust):9 task_set = MyTaskSet
@task使用可選的weight參數,該參數可用于指定任務的執行率。在以下示例中,task2的執行量是task1的兩倍:
1from locust import Locust, TaskSet, task2from locust.wait_time import between34class MyTaskSet(TaskSet):5 wait_time = between(5, 15)67 @task(3)8 def task1(self):9 pass1011 @task(6)12 def task2(self):13 pass1415class MyLocust(Locust):16 task_set = MyTaskSet
tasks屬性
使用@task裝飾器來聲明任務是一種友善的方法,通常也是最好的方法。但是,也可以通過設定tasks屬性來定義TaskSet的任務(使用@task裝飾器實際上隻填充tasks屬性)。
tasks屬性要麼是python可調用項的清單,要麼是
字典。這些任務是接收一個參數的python可調用函數——正在執行任務的TaskSet類執行個體。
這些任務是接收一個參數的python可調用函數——正在執行任務的TaskSet類執行個體。
1from locust import Locust, TaskSet23def my_task(l):4 pass56class MyTaskSet(TaskSet):7 tasks = [my_task]89class MyLocust(Locust):10 task_set = MyTaskSet
如果将tasks屬性指定為清單,那麼每次執行任務時,都将從tasks屬性中随機選擇該任務。但是,如果任務是字典(将可調用對象作為鍵,将整數作為值),則将随機選擇要執行的任務,但将int值作為比率。是以,任務看起來像這樣:{my_task: 3, another_task: 1}my_task被執行的可能性是other_task的3倍。
TaskSets可嵌套
TaskSet的一個非常重要的特性是它們可以嵌套,因為真實的網站通常以分層的方式建構,包含多個子部分。是以,嵌套任務集将允許我們定義一種行為,以更現實的方式來模拟使用者。例如,我們可以使用以下結構定義· - ----- TaskSet:
Main user behaviour
Watch movie
Filter movies
Read thread
Reply
New thread
View next page
Index page
Forum page
Browse categories
About page
嵌套TaskSet的方式就像使用task屬性指定任務時一樣,但不是引用python函數,而是引用另一個TaskSet:
1class ForumPage(TaskSet):2 @task(20)3 def read_thread(self):4 pass56 @task(1)7 def new_thread(self):8 pass910 @task(5)11 def stop(self):12 self.interrupt()1314class UserBehaviour(TaskSet):15 tasks = {ForumPage:10}1617 @task18 def index(self):19 pass
是以,在上面的示例中,如果在執行UserBehaviour TaskSet時選擇了要執行的ForumPage,則ForumPage TaskSet将開始執行。然後,ForumPage TaskSet将選擇其自己的任務之一,執行它,等待,依此類推。
關于上述示例,需要注意一點,那就是在ForumPage的stop方法中調用self.interrupt()。這樣做實際上是停止執行ForumPage任務集,并在UserBehaviour執行個體中繼續執行。如果我們在ForumPage的某處沒有調用interrupt()方法,Locust将永遠不會停止運作已經啟動的ForumPage任務。但是通過使用中斷功能,我們可以與任務權重一起定義模拟使用者離開論壇的可能性。
也可以使用@task裝飾器在類中内聯聲明嵌套的TaskSet,就像聲明普通任務時一樣:
1class MyTaskSet(TaskSet):2 @task3 class SubTaskSet(TaskSet):4 @task5 def my_task(self):6 pass
引用Locust執行個體,或父TaskSet執行個體
TaskSet執行個體的屬性locust指向它的locust執行個體,屬性parent指向它的父TaskSet(它将在基本TaskSet中指向Locust執行個體)。
TaskSequence類
TaskSequence類是一個TaskSet,但是它的任務是按順序執行的。要定義此順序,您應該執行以下操作:
1 @seq_task(1)2 def first_task(self):3 pass45 @seq_task(2)6 def second_task(self):7 pass89 @seq_task(3)10 @task(10)11 def third_task(self):12 pass
在上面的示例中,執行順序定義為先執行first_task,然後執行second_task,最後執行Third_task 10次。可以看到,可以使用@task裝飾器組合@seq_task,當然也可以将TaskSet嵌套在TaskSequences中,反之亦然。
Setups, Teardowns, on_start, 和on_stop
Locust還支援Locust級setup和teardown,TaskSet級setup和teardown,TaskSet'、 on_start和on_stop.
Setups 和 Teardowns
setup和teardown ,無論是在Locust還是TaskSet上運作,都是隻運作一次的方法。setup是在任務開始運作之前運作,而teardown是在所有任務完成并退出Locust之後運作的。這使你能夠在Locust任務運作之前執行一些準備工作(如建立資料庫),并在Locust退出之前進行清理(如删除資料庫)。
要使用它,隻需在Locust或TaskSet類上聲明一個setup或teardown。這些方法将為你運作。
on_start和on_stop方法
TaskSet類可以聲明on_start方法或on_stop方法。當模拟使用者開始執行該TaskSet類時,将調用on_start方法;而當TaskSet停止時,将調用on_stop
事件順序
由于許多設定和清除操作是互相依賴的,是以以下是它們的執行順序:
Locust setup (一次)
TaskSet setup (一次)
TaskSet on_start (每個locust一次)
TaskSet tasks…
TaskSet on_stop (每個locust一次)
TaskSet teardown (一次)
Locust teardown (一次)通常,setup和teardown方法應該是互補的。
發送HTTP請求
到目前為止,我們僅介紹了Locust使用者的任務排程部分。為了實際測試系統,我們需要發送HTTP請求。為了幫助我們做到這一點,存在HttpLocust類。當使用這個類時,每個執行個體獲得一個client屬性,該屬性将是HttpSession的一個執行個體,可用于發送HTTP請求。
HttpLocust類表示一個HTTP“使用者”,該“使用者”将被孵化并攻擊要加載測試的系統。此使用者的行為由task_set屬性定義,該屬性應指向TaskSet類。該類在執行個體化時建立一個client屬性,該屬性是一個HTTP client ,支援在請求之間保持使用者會話。
client= None在Locust執行個體化後建立的HttpSession執行個體。用戶端支援cookie,是以在HTTP請求之間的會話。
在繼承HttpLocust類時,我們可以使用它的client屬性對伺服器發出HTTP請求。這是一個Locust檔案的例子,可以用兩個URL負載測試站點 ;/ 和 /about/:
1from locust import HttpLocust, TaskSet, task, between23class MyTaskSet(TaskSet):4 @task(2)5 def index(self):6 self.client.get("/")78 @task(1)9 def about(self):10 self.client.get("/about/")1112class MyLocust(HttpLocust):13 task_set = MyTaskSet14 wait_time = between(5, 15)
使用上面的Locust類,每個模拟使用者将在請求之間等待5到15秒,并且 / 被請求的時間将是 /about/ 的兩倍。細心的讀者會發現奇怪的是,我們可以使用TaskSet中的self.client而不是self.locust.client來引用HttpSession執行個體。我們可以這樣做是因為TaskSet類有一個友善的屬性client,該屬性僅傳回self. custt .client。
使用HTTP用戶端
HttpLocust的每個執行個體在client屬性中都有一個HttpSession執行個體。HttpSession類實際上是request.Session的子類,可用于發出HTTP請求,該請求将使用get,post,put,put,delete,head,patch和options方法将其統計資料報給Locust。HttpSession執行個體将在請求之間儲存cookie,以便用于登入網站并在請求之間保持會話。也可以從Locust執行個體的TaskSet執行個體中引用client屬性,以便輕松地檢索用戶端并發出HTTP請求。
下面是一個簡單的示例,它向 / about 路徑發出GET請求(在這種情況下,我們假設self是TaskSet或HttpLocust類的執行個體:
1response = self.client.get("/about")2print("Response status code:", response.status_code)3print("Response content:", response.text)
這裡有一個POST請求的例子:
1response = self.client.post("/login",2 {"username":"testuser", 3 "password":"secret"}4 )
安全模式
HTTP用戶端配置為以safe_mode運作。這樣做的目的是,由于連接配接錯誤、逾時或類似原因而失敗的任何請求都不會引發異常,而是傳回一個空的虛拟Response對象。該請求将在Locust的統計資訊中标記為失敗。傳回的虛拟Response的content屬性将設定為None,其status_code=0。
手動控制請求是成功還是失敗
預設情況下,除非HTTP響應代碼為OK(<400),否則請求将被标記為失敗的請求。大多數情況下,此預設值是你想要的。但是,有時(例如,在測試URL端點時,你期望傳回404,或者在測試一個設計糟糕的系統時,即使出現錯誤也可能傳回200 OK)——需要手動控制Locust是否應該将請求标記為成功或失敗。
通過使用catch_response參數和with語句,即使響應代碼正确,也可以将請求标記為失敗:
1with self.client.get("/", catch_response=True) as response:2 if response.content != b"Success":3 response.failure("Got wrong response")
正如可以将具有OK響應代碼的請求标記為失敗一樣,也可以将catch_response參數與with語句一起使用,以标記導緻HTTP錯誤代碼的請求在統計中仍被報告為成功:
1with self.client.get("/does_not_exist/", catch_response=True) as response:2 if response.status_code == 404:3 response.success()
将帶有動态參數的URL請求分組
網站的url包含一些動态參數的頁面是很常見的。通常在Locust的統計資訊中将這些URL分組在一起是很有意義的。這可以通過将名稱參數傳遞給HttpSession的不同請求方法來完成。
例如:
1# 這些請求的統計資料将歸入以下類别: /blog/?id=[id]2for i in range(10):3 self.client.get("/blog?id=%i" % i, name="/blog?id=[id]")
公共庫--Common libraries
通常,将共享公共庫的多個locustfiles分組。在這種情況下,在項目中根目錄定義為調用Locust的目錄非常重要,建議所有的locust檔案都位于項目根目錄下。
平面檔案結構如下:
project root
commonlib_config.py
commonlib_auth.py
locustfile_web_app.py
locustfile_api.py
locustfile_ecommerce.py
locustfile可以使用以下指令導入公共庫,例如:import commonlib_auth。但是,這種方法并不能清楚地将公共庫與Locust檔案分開。
子目錄可以是一種更簡潔的方法(請參見下面的示例),但locust隻導入與運作的locustfile所在目錄相關的子產品。如果希望從項目根目錄(即運作locust指令的位置)導入,請確定在loucst檔案導入任何公共庫之前編寫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檔案可以使用以下方法導入公共庫:
1sys.path.append(os.getcwd())2import common.auth
Locust--分布式運作
如果一台計算機不足以模拟所需的使用者數量,那麼Locust将支援運作分布在多台計算機上的負載測試。
可以使用--master标志在主模式下啟動Locust的一個執行個體。這個執行個體将運作Locust的web接口,您可以在這裡啟動測試并實時檢視統計資訊。主節點本身不模拟任何使用者。相反,您必須使用--slave标志啟動一個或多個從Locust節點,與--master-host(指定主節點的IP /主機名)一起使用。
常見的設定是在一台機器上運作一個主程式,然後在從計算機上每個處理器核心運作一個從屬執行個體。
注意:在運作Locust分布式系統時,主計算機和每個從屬計算機都必須具有蝗蟲測試腳本的副本。
當分布式運作的時候,建議啟動的模拟使用者數量要大于Locust類的數量X從機的數量。否則,由于目前的實作,可能會得到與Locust類的weight屬性不對應的Locust類分布。而且,如果孵化率低于從屬節點的數量,則孵化将在“突發”中發生,其中所有從屬節點将孵化單個使用者,然後休眠數秒鐘,孵化另一個使用者,休眠并重複。
例如:要在master模式下啟動Locust:locust -f my_locustfile.py --master
選項--master将Locust設定為主模式, web接口将在此節點上運作。
-- slave将Locust設定為從屬模式。
--master-host=X.X.X.X可選,與-- slave一起使用,設定主節點的主機名/IP(預設為127.0.0.1)
--master-port=5557可選,與-- slave一起使用,用于設定主節點的端口号(預設為5557)。注意,locust将使用指定的端口,以及端口号+1。是以,如果使用5557,則locust将同時使用端口5557和5558。
--master-bind-host=X.X.X.X可選,與-- master一起使用。确定主節點将綁定到哪個網絡接口。預設為*(所有可用的接口)。
--master-bind-port=5557可選,與-- master一起使用。确定主節點将監聽的網絡端口。預設為5557。注意,locust将使用指定的端口,以及端口号+1。是以,如果使用5557,則locust将同時使用端口5557和5558。
--expect-slaves=X使用--no-web啟動主節點時使用。然後,主節點将一直等到連接配接了X個從節點之後才開始測試。
使用Docker進行分布式運作
詳見 性能測試Locust--(5)Docker運作 [詳見後文]
非UI模式下分布式運作Locust
詳見 性能測試Locust--(6)非UI模式下分布式運作Locust [詳見後文]
逐漸負載模式下分布式運作Locust
詳見 性能測試Locust--(4)逐漸負載模式 [詳見後文]
提高蝗蟲的性能
如果你打算運作大規模負載測試,你可能會對使用Locust附帶的備用HTTP client 感興趣。詳見 [Increase Locust’s performance with a faster HTTP client]https://docs.locust.io/en/stable/increase-performance.html#increase-performance
Locust--逐漸負載模式
如果想在不同的使用者負載下監控服務性能,并探測可以實作的最大tps,可以在啟用“逐漸負載”模式運作Locust--stp-load
1$ locust -f locust_files/my_locust_file.py --step-load
選項
--step-load啟用“Step Load--逐漸負載”模式, 以監視當使用者負載增加時性能名額如何變化。
--step-clients在“逐漸負載”模式下,用戶端數量逐漸增加。與--step-load一起使用。
--step-time在“逐漸負載”模式下,每個Step的進持續時間,例如(300s,20m,3h,1h30m等)。與--step-load一起使用。
Locust在非Web UI的情況下“逐漸負載”模式運作
如果要在沒有Web UI的情況下以逐漸負載方式運作Locust,則可以使用--step-clients和--step-time來執行此操作:
1$ locust -f --no-web -c 1000 -r 100 --run-time 1h30m --step-load --step-clients 300 --step-time 20m
Locust 将逐漸蜂擁而至,一旦時間到了,将關閉它們。
Locust在逐漸負載模式下分布式運作
如果你想要在逐漸負載模式下分布式運作Locust,你應該在啟動主節點時指定--step-load選項,來分步式聚集locust。然後在Locust UI中将顯示 --step-cients 選項和 --step-time 選項。
Locust--Docker運作Locust
為了簡單起見,我們提供了一個可以獨立運作的Docker映像,可以作為主映像,也可以作為從映像。
環境變量
LOCUST_MODEstandalone、master 或者 slave。預設是standalone。
LOCUSTFILE_PATH容器内部到locustfile的路徑。預設為/locustfile.py.
LOCUST_MASTER_HOSTmaster的主機名。
LOCUST_MASTER_PORT與master通信的端口。預設為5557
LOCUST_OPTS傳遞給Locust的其他選項。預設為''
運作測試
運作測試最簡單的方法是使用内置的測試檔案建構映像。一旦編寫了locustfile,就可以使用一個簡單的Dockerfile将其打包到Docker映像中:需要将建構的映像推送到Dockerhub,AWS ECR或GCR等Docker存儲庫中,以便分布式基礎架構能夠提取該鏡像。請參閱所選存儲庫的文檔,以了解如何通過存儲庫進行身份驗證以擷取鏡像。
為了在本地調試,您可以運作一個容器并将locustfile作為volume傳遞進來:
要在沒有Web UI的獨立模式下運作,可以使用LOCUST_OPTS環境變量添加所需的選項:
如果您是Kubernetes使用者,則可以使用[Helm chart]https://github.com/helm/charts/tree/master/stable/locust
Locust--非UI模式下運作Locust
可以在沒有Web UI的情況下運作Loccust.例如,如果要以某種自動化流程(例如CI伺服器)運作Locust,通過使用--no-web标記和-c和-r:
1$ locust -f locust_files/my_locust_file.py --no-web -c 1000 -r 100
-c 指定要生成的Locust使用者數;-r 指定孵化率(每秒産生的使用者數)。
為測試設定時間限制
如果要指定測試的運作時間,可以使用 --run-time 或者 -t:
1$ locust -f --no-web -c 1000 -r 100 --run-time 1h30m
一旦時間到,Locust将關閉。
允許任務在關閉時完成其疊代
預設情況下,Locust将立即停止任務。如果想讓任務完成疊代,則可以使用--stop-timeout 。
1$ locust -f --no-web -c 1000 -r 100 --run-time 1h30m --stop-timeout 99
非Web UI的情況下分布式運作Locust
在非UI情況下想分布式運作Locust,應該在啟動主節點時指定--expect-slaves選項,指定希望連接配接的從節點的數量。然後,将一直等到連接配接了諸多從節點之後才開始測試。
Locust--使用更快的HTTP client提高Locust性能
Locust預設的HTTP用戶端使用python-requests。因為requests是一個維護良好的python程式包,它提供了許多python開發人員都熟悉的優質API。是以,在許多情況下,建議使用使用請求的預設HttpLocust。
但是,如果計劃運作大規模的測試,Locust提供了一個替代的HTTP 用戶端 FastHttpLocust,它使用geventhttpclient而不是requests。
該用戶端的速度明顯提高,其性能是HTTP-requests的5 ~ 6倍。這并不一定意味着每個CPU核心可以模拟的使用者數量将自動增加5 ~ 6倍,因為這還取決于負載測試腳本。
總之,如果你的Locust腳本在執行HTTP-requests時花費了大量的CPU時間,那麼你可能會看到顯著的性能提升。
如何使用FastHttpLocust
代替HttpLocust的子類是FastHttpLocust:
1from locust import TaskSet, task, between2from locust.contrib.fasthttp import FastHttpLocust34class MyTaskSet(TaskSet):5 @task6 def index(self):7 response = self.client.get("/")89class MyLocust(FastHttpLocust):10 task_set = MyTaskSet11 wait_time = between(1, 60)
注意與預設python-requests的HttpLocust相比,FastHttpLocust使用一個完全不同的API實作另一個HTTP client 。是以,取決于如何使用HttpClient,FastHttpLocust可能無法替代HttpLocust。在FastHttpLocust的用戶端實作中,SSL域名檢查被關閉。是以,它将允許通過無效的SSL證書而不會産生任何問題。
API
FastHttpSession類
classFastHttpSession(base_url, **kwargs)
init(base_url, **kwargs)
x.init(…) 初始化X,詳見 help
get(path, **kwargs)
發送GET請求
head(path, **kwargs)
發送HEAD請求
options(path, **kwargs)
發送OPTION請求
patch(path, data=None, **kwargs)
發送PATCH請求
post(path, data=None, **kwargs)
發送POST請求
put(path, data=None, **kwargs)
發送PUT請求
request(method, path, name=None, data=None, catch_response=False, stream=False, headers=None, auth=None, **kwargs)發送HTTP請求并傳回locust.contrib.fasthttp.FastResponse對象.
Parameters:
method--建立新Request對象方法。
path--Path将與已指定的base host URL連接配接。也可以是完整URL,在這種情況下,将請求完整URL,并且忽略host。
name--可選,在Locust的統計資訊中可以指定一個參數作為标簽,而不是URL路徑。這可用于将請求的不同URL分組到Locust的統計資訊中的單個條目中。
catch-response--可選,Boolean,如果設定了Boolean參數,可用于使請求傳回上下文管理器,以用作with語句的參數。即使響應代碼正常(2xx),也可以根據響應的内容将請求标記為失敗。反之亦然,即使沒有響應代碼(即500或404),也可以使用catch_response來捕獲請求,然後将其标記為成功。
data-- 可選,請求體-body, Dict或bytes
headers-- 可選,請求頭,Dict
auth-- 可選,驗證(使用者名,密碼)元組,以啟用基本HTTP驗證。
stream-- 可選,如果設定為true,則不會立即使用響應主體,而是可以通過通路Response對象上的stream屬性來使用它。将流設定為True的另一個作用是:不會将下載下傳響應内容的時間記錄到Locust報告中的請求時間。
classFastResponse(ghc_response, request=None, sent_request=None)
contentUnzips, 如有必要,并緩沖接收到的Body. 小心大檔案!
headers= None類似于Dict的對象,包含響應标頭
text以解碼字元串的形式傳回響應的文本内容(python2上的unicode)
Locust--CSV存儲測試資料
Locust的測試結果儲存到CSV檔案,在這種情況下,有兩種方法可以執行此操作。
首先,通過Web UI運作Locust時,可以在“Download Data”頁籤下得到CSV檔案。
其次,可以使用标簽運作Locust,該标簽将定期儲存兩個CSV檔案。如果計劃使用--no-web标簽以自動化方式運作Locust,這将特别有用:
1$ locust -f examples/basic.py --csv=example --no-web -t10m
檔案将被命名為example_response_times.csv 和 example_stats.csv (使用--csv=example)并記錄Locust建構的資訊。
如果你想要更快(慢)的寫入速度,也可以自動以寫入頻率:
1import locust.stats2# 預設為2秒3locust.stats.CSV_STATS_INTERVAL_SEC = 5
此資料将寫入兩個檔案,并将_response_times.csv和_stats.csv添加到你提供的名稱中:
1$ cat example_response_times.csv2"Name","# requests","50%","66%","75%","80%","90%","95%","98%","99%","99.9%","99.99%","100%"3"GET /",31,4,4,4,4,4,4,4,4,4,4,44"/does_not_exist",0,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A"5"GET /stats/requests",38,3,4,4,4,4,5,5,5,5,5,56"None Total",69,3,4,4,4,4,4,5,5,5,5,5
和
1$ cat example_stats.csv2"Type","Name","# requests","# failures","Median response time","Average response time","Min response time","Max response time","Average Content Size","Requests/s"3"GET","/",51,0,4,3,2,6,12274,0.894"GET","/does_not_exist",0,56,0,0,0,0,0,0.005"GET","/stats/requests",58,0,3,3,2,5,1214,1.016"None","Total",109,56,3,3,2,6,6389,1.89
使用定制的用戶端測試其他系統
以HTTP為主要目标建構Locust。但是,通過編寫觸發request_success和request_failure事件的自定義用戶端,可以很容易的将其擴充,用來對任何基于request/response的系統進行負載測試。
Locust client示例--XML-RPC
以下是Locust類XmlRpcLocust的示例,該類提供XML-RPC用戶端XmlRpcClient并跟蹤所有發出的請求:
1import time2import xmlrpclib34from locust import Locust, TaskSet, events, task, between567class XmlRpcClient(xmlrpclib.ServerProxy):8 """9 Simple, sample XML RPC client implementation that wraps xmlrpclib.ServerProxy and 10 fires locust events on request_success and request_failure, so that all requests 11 gets tracked in locust's statistics.12 """13 def __getattr__(self, name):14 func = xmlrpclib.ServerProxy.__getattr__(self, name)15 def wrapper(*args, **kwargs):16 start_time = time.time()17 try:18 result = func(*args, **kwargs)19 except xmlrpclib.Fault as e:20 total_time = int((time.time() - start_time) * 1000)21 events.request_failure.fire(request_type="xmlrpc", name=name, response_time=total_time, exception=e)22 else:23 total_time = int((time.time() - start_time) * 1000)24 events.request_success.fire(request_type="xmlrpc", name=name, response_time=total_time, response_length=0)25 # In this example, I've hardcoded response_length=0. If we would want the response length to be26 # reported correctly in the statistics, we would probably need to hook in at a lower level2728 return wrapper293031class XmlRpcLocust(Locust):32 """33 This is the abstract Locust class which should be subclassed. It provides an XML-RPC client34 that can be used to make XML-RPC requests that will be tracked in Locust's statistics.35 """36 def __init__(self, *args, **kwargs):37 super(XmlRpcLocust, self).__init__(*args, **kwargs)38 self.client = XmlRpcClient(self.host)394041class ApiUser(XmlRpcLocust):4243 host = "http://127.0.0.1:8877/"44 wait_time = between(0.1, 1)4546 class task_set(TaskSet):47 @task(10)48 def get_time(self):49 self.client.get_time()5051 @task(5)52 def get_random_number(self):53 self.client.get_random_number(0, 100)
如果你以前編寫過 Locust的測試,你應該知道一個名為 ApiUser 的類,它是一個普通的 Locust 類,它的 task_set屬性是一個 TaskSet 類的子類,而這個子類帶有多個 task。
然而,ApiUser 繼承自 XmlRpcLocust,您可以在 ApiUser 的正上方看到它。XmlRpcLocust 類在 client 屬性下提供 XmlRpcClient 的執行個體。XmlRpcClient 是标準庫的 xmlrclib. serverproxy的裝飾器。它基本上隻是代理函數調用,但是添加了觸發用于将所有調用報告給 Locust 統計資料的 locust.events.request_success和 locust.events.request_failure的重要功能。
下面是 XML-RPC 伺服器的實作,它可以作為上述代碼的伺服器:
1import random2import time3from SimpleXMLRPCServer import SimpleXMLRPCServer456def get_time():7 time.sleep(random.random())8 return time.time()910def get_random_number(low, high):11 time.sleep(random.random())12 return random.randint(low, high)1314server = SimpleXMLRPCServer(("localhost", 8877))15print("Listening on port 8877...")16server.register_function(get_time, "get_time")17server.register_function(get_random_number, "get_random_number")
Locust--Locust API以及延伸擴充--0.14.4
Locust延伸擴充
Locust附帶了很多事件,這些事件提供了以不同方式擴充Locust的鈎子(hooks )。
事件監聽器可以在子產品級注冊到Locust檔案中。這裡有一個例子:
1from locust import events23def my_success_handler(request_type, name, response_time, response_length, **kw):4 print "Successfully fetched: %s" % (name)56events.request_success += my_success_handler
注意:強烈建議你在偵聽器中添加通配符關鍵字參數(上面代碼中的** kw),以防止在以後的版本中添加新參數時代碼崩潰。要檢視所有可用事件,請參閱事件[挂鈎](https://docs.locust.io/en/stable/api.html#events)。
添加網絡路由
Locust使用Flask來提供Web UI,是以很容易将Web端點添加到Web UI。隻需将Flask應用程式導入您的locustfile并設定一條新路徑:
1from locust import [email protected]("/added_page")4def my_added_page():5 return "Another page"
現在應該能夠啟動locust并浏覽到:http://127.0.0.1:8089/added_page
Locust日志記錄
Locust附帶有基本的日志記錄配置,該配置可以選擇使用--loglevel和/或--logfile來修改配置。如果要控制日志記錄配置,則可以提供--skip-log-setup标志,該标志将忽略其他參數。
Options選項
--skip-log-setup禁用Locust的日志記錄設定。相反,配置是由Locust test或Python預設設定提供配置。
--loglevel在DEBUG/INFO/WARNING/ERROR/CRITICAL之間選擇。預設值為INFO。簡寫為-L。
--logfile日志檔案的路徑。如果未設定,則日志将轉到stdout / stderr。
Locust API
API的話以官網為準,以下是官網連結
API 官網連結 :https://docs.locust.io/en/stable/api.html
Locust 類
class Locust
表示一個策劃并将要對系統進行負載測試的“使用者”。此使用者的行為由task_set屬性定義,該屬性應該指向TaskSet類。此類通常應由定義某種用戶端的類繼承。例如,在對HTTP系統進行負載測試時,您可能希望使用HttpLocust類。task_set= NoneTaskSet類,定義此Locust的執行行為
wait_time= None該方法,傳回執行Locust任務之間的時間(以秒為機關)。可以為單個TaskSet覆寫。例如:
1from locust import Locust, between2class User(Locust):3 wait_time = between(3, 25)
weight= 10Locust被選中執行的機率。權重越高,被選中的機會越大。
HTTPLocust 類
class HttpLocust
表示一個策劃并将要對系統進行負載測試的HTTP“使用者”。該HTTP使用者的行為由task_set屬性定義,該屬性應該指向TaskSet類。這個類在執行個體化時會建立一個 client 屬性,這個屬性的值是一個支援在請求間保持使用者會話(user session)的 HTTP 用戶端。
client=None在 locust 執行個體化時建立的 HttpSession 執行個體。用戶端支援 cookies,是以在 HTTP 請求間保持會話。
TaskSet類
class TaskSet(parent)
該類定義locust使用者将要執行的一組任務。
當 TaskSet開始運作時,它将從 tasks 屬性中選擇一個任務并執行,然後調用這個任務的 wait_function 方法,之後再調用另一個任務,以此類推。其中 wait_function 方法定義并傳回一個以毫秒為機關的睡眠時間,wait_function 方法定義的睡眠時間的預設是介于 min_wait 和 max_wait 之間且均勻分布的随機數;然後它将排程另一個任務執行,等等。
TaskSets可以嵌套,這意味着一個 TaskSet 的 tasks 屬性可以包含其他的 TaskSet。如果計劃執行嵌套的 TaskSet ,則将執行個體化它并從目前執行的 TaskSet 進行調用。然後,目前運作的 TaskSet 中的執行将被移交給嵌套的 TaskSet ,嵌套的 TaskSet 将繼續運作,直到遇到由 TaskSet.interrupt() 方法抛出的 InterruptTaskSet 異常時終止。(然後在第一個任務集中繼續執行)。
client引用根Locust執行個體的client屬性。
interrupt(reschedule=True)中斷TaskSet并将執行控制移交給父TaskSet。
如果reschedule的值為True,父 locust 将立即重新排程并執行下一個任務。
這個方法不應該由根 TaskSet(即立即附加到 Locust 類的 task_set 屬性)調用,而應該由層次結構中更深層次的嵌套 TaskSet 類調用。
locust= None當TaskSet執行個體化後,會引用根Locust類執行個體。
parent= None執行個體化TaskSet時,将引用父TaskSet或Locust類執行個體。對于嵌套TaskSet類很有用。
schedule_task(task_callable, args=None, kwargs=None, first=False)将任務添加到Locust的任務執行隊列中。參數:
task_callable: 要排程的Locust任務計劃表
args: 将傳遞給可調用任務(task_callable)的參數
kwargs: 關鍵字字典參數,将傳遞給可調用(task_callable)的任務.
first: 可選關鍵字參數。如果為 True,任務會被放到隊列的首位。
tasks= []清單中包含表示 locust 使用者任務的可調用對象。
如果該參數值是一個清單,那麼将從中随機挑選任務進行執行。
如果該參數值是一個元素為二進制組 (callable, int) 的清單或元素為 callable: int 的字典,那麼将随機選擇要執行的任務,但是每個任務将根據其對應的 int 類型的值進行權重。是以在下面的例子中,ThreadPage 被選中的可能性是 write_post 的15倍:
1class ForumPage(TaskSet):2 tasks = {ThreadPage:15, write_post:1}
wait_time()該方法傳回執行任務之間的時間(以秒為機關)。例如:
1from locust import TaskSet, between2class Tasks(TaskSet):3 wait_time = between(3, 25)
任務裝飾器 task decorator
task(weight=1)使用一個便捷裝的飾器,以便能夠為類中的TaskSet内聯聲明任務。
1class ForumPage(TaskSet):2 @task(100)3 def read_thread(self):4 pass56 @task(7)7 def create_thread(self):8 pass
TaskSequence類(任務序列類)
classTaskSequence(parent)定義 locust 使用者将要執行的任務序列。
當 TaskSequence 開始執行時,它将從 tasks 屬性值中根據任務的索引選擇一個任務進行執行,然後調用它的定義了一個睡眠時間的 wait_fucntion 方法。wait_function 定義的睡眠時間預設為介于 min_wait 和 max_wait 之間且均勻分布的一個随機數,機關為毫秒。然後再調用索引為 index + 1 / % 的任務,以此類推。
TaskSequence 可以與 TaskSet 嵌套,這意味着 TaskSequence 的 tasks 屬性可以包含 TaskSet 執行個體和其他TaskSequence 執行個體。如果計劃執行嵌套的 TaskSet,則将執行個體化它并從目前執行的 TaskSet 調用它。然後,目前運作的 TaskSet 中的執行将被移交給嵌套的 TaskSet ,這個嵌套的 TaskSet 将繼續運作,直到遇到由 TaskSet.interrupt()抛出 InterruptTaskSet異常時終止,然後在第一個 TaskSet 中繼續執行。
在這個類中,任務應該被定義成一個清單,或者簡單地由 @task_seq 裝飾器定義。
client引用根 Locust 執行個體的client屬性。
interrupt(reschedule=True)中斷 TaskSet 并将執行控制權交給父 TaskSet。
如果 reschedule 的值為 True,父 locust 将立即重新排程并執行下一個任務。
這個方法不應該由根 TaskSet (即立即附加到 Locust 類的 task_set 屬性)調用,而應該由層次結構中更深層次的嵌套 TaskSet 類調用。
schedule_task(task_callable, args=None, kwargs=None, first=False)添加一個任務到Locust 的任務執行隊列。參數:
task_callable:要排程的 locust 任務。
args:要傳遞給 task_callable 的參數。
kwargs:要傳遞給 task_callable 的關鍵字參數的字典。
first:可選關鍵字參數。如果為 True,任務會被放到隊列的首位。
wait_time()該方法傳回執行任務之間的時間(以秒為機關)。例如:
1from locust import TaskSet, between2class Tasks(TaskSet):3 wait_time = between(3, 25)
seq_task裝飾器
seq_task(order)用于在類中内聯聲明 TaskSequence 的任務。例如:
1class NormalUser(TaskSequence):2 @seq_task(1)3 def login_first(self):4 pass56 @seq_task(2)7 @task(25) # You can also set the weight in order to execute the task for `weight` times one after another.8 def then_read_thread(self):9 pass1011 @seq_task(3)12 def then_logout(self):13 pass
HttpSession 類
between(min_wait, max_wait)傳回一個函數,該函數将在min_wait和max_wait之間傳回一個随機數。例如:
1class User(Locust):2 # wait between 3.0 and 10.5 seconds after each task3 wait_time = between(3.0, 10.5)
constant(wait_time)傳回一個函數,該函數隻傳回wait_time參數指定的數字。例如:
1class User(Locust):2 wait_time = constant(3)
constant_pacing(wait_time)傳回一個函數,該函數将跟蹤任務的運作時間,每次調用它時,它将傳回一個等待時間,該等待時間将嘗試使任務執行之間的總時間等于wait_time參數指定的時間。
在以下示例中,無論任務執行時間如何,任務總是每秒執行一次:
1class User(Locust):2 wait_time = constant_pacing(1)3 class task_set(TaskSet):4 @task5 def my_task(self):6 time.sleep(random.random())
如果任務執行超過了指定的wait_time,則在開始下一個任務之前的等待時間為0。
HttpSession 類
**class HttpSession(base_url, *args, *kwargs)*用于執行Web請求和在請求之間保留(會話)Cookie的類(以便能夠登入和登出網站)。每個請求都被記錄下來,以便Locust可以顯示統計資訊。
這是Python的 requests 庫的requests.Session類的拓展,工作原理與是極其相似的。然而,發送請求的方法(get、post、delete、put、head、options、patch、request)現在可以接受一個 url 參數,這個參數隻是 URL的路徑部分,在這種情況下,URL的主機部分将取 HttpSession.base_url (繼承自一個 Locust 類的 host 屬性)的值。
發送請求的每個方法還接受兩個額外的可選參數,這些參數是特定于 Locust ,在Python的 requests 庫中不存在的:參數:
name可選參數。可以指定為 Locust 的統計資訊中的标簽,用于代替 URL 路徑。這可以用于将被請求的不同 URL 分組到 Locust 統計資料中的一個條目中。
catch_response可選參數。如果要設定,可以是一個布爾值。可以用來使請求傳回為作為with 語句的參數的上下文管理器。這将允許根據響應内容将請求标記為失敗,即使響應代碼是 ok (2xx) ,反之亦然。可以使用 catch_response 捕捉請求,然後将其标記為成功,即使響應代碼不是 ok (例如 500 或 404)。
__init__(base_url, \*args, \*\* kwargs)x.init(…) 初始化x; see help(type(x)) for signature
delete(url, \*\*kwargs)發送一個 DELETE 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。**kwargs:request 的可選參數。傳回值類型:requests.Response 對象。
get(url, \*\*kwargs)發送一個 GET 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。**kwargs:request 的可選參數。傳回值類型:requests.Response 對象。
head(url, \*\*kwargs)發送一個 HEAD 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。**kwargs:request 的可選參數。傳回值類型:requests.Response 對象。
options(url, \*\*kwargs)發送一個 OPTIONS 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。**kwargs:request 的可選參數。傳回值類型:requests.Response 對象。
patch(url,data=None , \*\*kwargs)發送一個 PATCH 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。data:可選參數。發送到請求主體中的字典、bytes 或 file-like 對象。**kwargs:request 的可選參數。傳回值類型:requests.Response 對象。
post(url,data=None , json=None, \*\*kwargs)發送一個 POST 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。data:可選參數。發送到請求主體中的字典、bytes 或 file-like 對象。**kwargs:request 的可選參數。json:可選參數。要發送到請求主體中的 json 格式資料。傳回值類型:requests.Response 對象。
put(url,data=None , \*\*kwargs)發送一個 PUT 請求,傳回一個 Response 對象。參數:url:新 Request 對象的URL。data:可選參數。發送到請求主體中的字典、bytes 或 file-like 對象。**kwargs:request 的可選參數。傳回值類型:requests.Response 對象。
request(method, url, name=None , catch_response=False, **kwargs)構造并發送一個requests.Request 。傳回 requests.Response對象。
參數:method:新 Request 對象的方法。url:新 Request 對象的URL。name:可選參數。可以指定為 Locust 的統計資訊中的标簽,用于代替 URL 路徑。這可以用于将被請求的不同 URL 分組到 Locust 統計資料中的一個條目中。catch_response可選參數。如果要設定,可以是一個布爾值。可以用來使請求傳回為作為with 語句的參數的上下文管理器。這将允許根據響應内容将請求标記為失敗,即使響應代碼是 ok (2xx) ,反之亦然。可以使用 catch_response捕捉請求,然後将其标記為成功,即使響應代碼不是 ok (例如 500 或 404)。params:可選參數。要發送到Request的查詢字元串的字典或 bytes 對象。data:可選參數。要發送到 Request主體中的字典或 bytes 對象。headers:可選參數。與 Request一起發送的表示 HTTP headers 的字典。cookies:可選參數。與 Request一起發送的表示 cookies 的 dict 或 CookieJar 對象。files:可選參數。用于多部分編碼上傳的元素為 filename: filename: file-like-objects 的字典。auth:可選參數:用于啟用 Basic/Digest或自定義的 HTTP Auth 的元組或可調用對象。timeout:可選參數。以浮點數或(連接配接逾時、讀取逾時)元組的形式等待伺服器發送資料的時間(以秒為機關)。allow_redirects:可選參數。布爾類型。預設值為 True。表示是否允許重定向。proxies:可選參數。字典類型。鍵表示代理使用的協定,鍵值表示代理的URL。stream:可選參數。是否立即下載下傳響應内容。預設值為 False。verify:可選參數。如果為True,則會驗證 SSL 證書。也可以提供一個 CA_BUNDLE 路徑。cert:可選參數。如果提供一個字元串。那麼應該是指向SSL 用戶端證書(.pem檔案)的路徑;如果是一個元組,則應該是 (‘cert’, ‘key’)。
Response類
這個類其實是位于python-requests庫中的,但是由于 Locust在構造HTTP 請求的時候要用到這個類,并且在編寫 Locust負載測試時,這個類也非常重要,是以就把這個類包含在了 API 文檔裡。您還可以檢視請求文檔中的Response類。
class ResponseResponse對象,它包含伺服器對HTTP請求的響應。
apparent_encoding明顯的編碼,由chardet庫提供。
close()用于釋放連結。一旦調用此方法,就不能再次通路基礎raw對象。注意:通常不應該顯式調用它。
content響應的内容,以位元組為機關。
cookies= None伺服器發回的一堆cookie。
elapsed= None發送請求到響應到達之間的時間間隔(使用 timedelta 對象表示)。此屬性專門度量從發送請求的第一個位元組到完成對報頭的解析所花費的時間。是以,它不受響應内容或 stream 關鍵字參數值的影響。
encoding= None通路 r.text 時解碼操作要用到的編碼方式。
headers= None不區分大小寫的響應頭字典。例如,headers['content-encoding'] 将會傳回響應頭中鍵為 Content-Encoding 的鍵值。
history= None請求曆史記錄中的響應對象清單。任何重定向響應都将在這裡結束。該清單從最早的請求到最近的請求進行排序。
is_permanent_redirect如果此響應是重定向的永久版本之一,則傳回 True,否則傳回 False。
is_redirect如果此響應是可以自動處理的格式良好的HTTP重定向(通過 session.resolve_reredirect() 判斷),則傳回True,否則傳回 False。
iter_content(chunk_size=1, decode_unicode=False)疊代響應資料。當對請求設定stream=True時,這可以避免立即将内容讀入記憶體以獲得較大的響應。資料塊大小是應該讀入記憶體的位元組數。這不一定是解碼時傳回的每個項的長度。chunk_size的類型必須是int或None。None的值将根據流的值發揮不同的功能。stream=True将在到達資料塊時讀取資料,無論塊的大小如何。如果stream=False,則以單個塊的形式傳回資料。
如果decode_unicode為True,那麼将使用基于響應的最佳可用編碼對内容進行解碼。
iter_lines(chunk_size=512, decode_unicode=False, delimiter=None)疊代響應資料,一次一行。當對請求設定stream=True時,這可以避免立即将内容讀入記憶體以獲得較大的響應。注意:這種方法是不安全的。
json(**kwargs)傳回響應的 json 編碼内容(如果有的話)。**kwargs-- 表示要傳給jason.loads函數的可選參數。ValueError --如果響應的主體中不包含有效的 json 資料,則将引發 ValueError 異常。
links傳回已解析的響應頭連結(如果有的話)。
next傳回一個PreparedRequest 對象,用于表示重定向鍊中的下一個請求(如果有的話)。
ok如果 status_code 小于400,傳回 True;如果不小于400,傳回 False。
此屬性檢查響應的狀态代碼是否在400到600之間,以檢視是否存在用戶端錯誤或伺服器錯誤。如果狀态碼在200到400之間,則傳回 True ,而不是檢查響應代碼是否為 200 OK。
raise_for_status()如果發生HTTPError,則引發存儲的HTTPError。
reason= None響應HTTP狀态的文本原因.例如:“Not Found” 或者 “OK”.
request= None這是響應的PreparedRequest對象。
status_code= None響應的HTTP狀态的整數代碼。例如404或200。
text使用Unicode字元表示的響應的内容。如果 Response.encoding 是 None,則使用 chardet猜測編碼。響應内容的編碼按照 RFC 2616 的規定,由 HTTP headers 唯一确定。如果可以利用非 HTTP 知識更好地猜測編碼,應該在通路該特性之前為r.encoding設定合适的值。
url= None響應的最終URL位置。
ResponseContextManager類
class ResponseContextManager(response)
可以充當上下文管理器的 Response 類,提供手動控制HTTP 請求在在 Locost 的統計資料中應該标記為成功還是失敗的能力。
這個類是 Response 類的子類。包含兩個額外的方法:success 和 failure。
failure(exc)将響應報告為失敗。其中參數 exc 可以是一個Python的異常類或者一個字元串。如果是一個字元串,那麼将使用這個字元串來執行個體化 CatchResponseError 類。例如:
1with self.client.get("/", catch_response=True) as response:2 if response.content == b"":3 response.failure("No data")
success()報告響應成功例如:
1with self.client.get("/does/not/exist", catch_response=True) as response:2 if response.status_code == 404:3 response.success()
InterruptTaskSet異常exception InterruptTaskSet(reschedule=True)在 Locust 任務内抛出這個異常時,将會中斷這個 Locust 正在執行的目前任務。
事件鈎子
事件鈎子都是 locust.events.EventHook 類的執行個體。
class EventHook簡單事件類,用于為 locust 中不同類型的事件提供鈎子。
下面的代碼示範如何使用這個類:
1my_event = EventHook()2def on_my_event(a, b, **kw):3 print "Event was fired with arguments: %s, %s" % (a, b)4my_event += on_my_event5my_event.fire(a="foo", b="bar")
如果 reverse 的值為 True,則處理程式将按照插入時的相反順序運作。
注意: 強烈建議你在事件監聽器中添加通配符關鍵字參數,以防止在以後的版本中添加新參數時代碼中斷。
可用的鈎子
下面的事件鈎子在 locust.events 子產品下可用:request_success= 當一個請求成功完成時觸發。監聽者應該使用如下參數:
request_type:使用的請求方法。
name:被調用的URL的路徑(如果在對用戶端的調用中使用了名稱,則重寫名稱)。
response_time:使用毫秒表示的響應時間。
response_length:響應的 Content-Length 值。
request_failure= 當一個請求失敗時觸發。事件觸發式将使用如下參數:
request_type:使用的請求方法。
name:被調用的URL的路徑(如果在對用戶端的調用中使用了名稱,則重寫名稱)。
response_time:用毫秒表示的從送出請求到抛出異常時的時間間隔。
exception:抛出的異常的執行個體。
locust_error= 當 Locust 類的執行過程中出現異常時觸發。事件觸發式将使用如下參數:
locust_instance:異常發生時的 Locust 類的執行個體。
exception:抛出的異常。
tb:回溯對象(從 sys.exc_info()[2] 得到)
report_to_master= 當 Locust 在 -slave 模式下運作時使用。用于将資料附加到定期發送給主伺服器的資料字典上。當報告要發送到主伺服器時,它會定期觸發。
注意: Locust 使用的鍵 ‘stats’ 和 ‘errors’ 不應該被覆寫。
事件觸發式将使用如下參數:
client_id:正在運作的 Locust 程序的用戶端 ID。
data:可修改的資料字典,以便附加應發送到主伺服器的資料。
slave_report= 當 locust 在 -master 模式下運作使用。并在 Locust 主伺服器從從屬伺服器收到報告時觸發。此事件可用于聚合來自 Locust 從屬伺服器的資料。事件觸發式将使用如下參數:
client_id:報告 Locust 從屬伺服器的用戶端 ID。
data:來自從屬節點的資料字典。
hatch_complete= 當所有 locust 使用者都已經生成時觸發。事件觸發式将使用如下參數:
user_count:孵化出的使用者數量。
quitting= 在退出 locust 程序時觸發。
第三方工具
支援其他采樣器協定,報告等。Locust 插件:https://github.com/SvenskaSpel/locust-plugins/
無需手動步驟即可自動執行分布式運作Locust叢集:https://github.com/SvenskaSpel/locust-swarm/(蝗蟲群)
使用其他語言Locust主伺服器和Locust從伺服器通過交換msgpack消息進行通信,這是許多語言所支援的。是以,您可以使用任何喜歡的語言編寫Locust任務。為了友善起見,一些庫充當了從屬運作程式。他們運作你的Locust任務,并定期向master報告。
GolangBoomer:https://github.com/myzhan/boomer/
JavaLocust4j:https://github.com/myzhan/locust4jSwarm: https://github.com/anhldbk/swarm
配置管理部署Locust很容易,但是有些工具仍然可以提供一定程度的便利。
tinx.locust是Ansible的一個安裝角色,用于配置和控制Locust系統服務,或使用ansible-container建構Locust docker映像。還管理locustfile和相應的測試資料。
文章合集
免費福利 視訊教程
往期性能推文:
萬水千山總是情,點個“在看”行不行!!?