原文連結:https://mp.weixin.qq.com/s/9PxSPuHmucSLi_welq6uNQ
現在性能測試工具太多,根據業務不同使用,比如說我們熟悉的loadrunner、jmeter、ab、webbench等等,這些工具是也主流大部分也在使用,但是如果你能看懂python代碼,會寫就更好了,可以根據自已的業務編寫,不會自己寫架構也可以嘗試一下今天的主角Locust,一款基于python的開源性能測試工具,主要也是負載測試工具,它的優點是學習起來比較簡單,功能完全自定制,自由控制業務邏輯,使用比較靈活,支援分布式。
它主要就是模拟一群使用者将通路你的背景。每個使用者的行為由你編寫的python代碼定義,同時可以從Web界面中實時觀察到使用者的行為。
Locust完全是事件驅動的,是以在單台機器上能夠支援幾千并發使用者通路。
與其它許多基于事件的應用相比,Locust并不使用回調,而是使用gevent,而gevent是基于協程的,可以用同步的方式來編寫異步執行的代碼。每個使用者實際上運作在自己的greenlet中。
所有的性能測試工具都至少包含這3方面:
1、壓力産生器,也就是可以指定産生多大的壓力,多少并發;
2、資料統計,也就是結果的展示,要統計TPS【QPS】是多少,響應時間多少等等,這些資料;
3、代理功能,代理功能簡單來說就4個字,分攤壓力:如說你壓測的時候要用1000個并發,但是你的電腦(壓力機)配置比較弱,隻支援500并發,再大電腦就死掉了,完犢子,壓測不了。那怎麼辦呢,就得分攤壓力,從其他伺服器發壓力,那就可以了,每台伺服器上500個并發,人多好幹活嘛,代理就是幹這個的,把這個上面的壓力分攤到别的電腦上。
當然今天的主角Locust這3個功能都是有的,locust的官網是 www.locust.io,有詳細的文檔檢視。
Locust其實是python的一個第三方子產品,安裝很簡單,直接pip install locust即可,或者自己下載下傳安裝包,手動安裝。安裝完成後,就有locust指令,在指令行裡面輸入 locust --help,有幫助資訊就安裝成功了。
如何用呢,很簡單,隻需要幾行代碼就可以實作,并且有漂亮的web界面也可用指令行執行,可以設定并發數,和檢視結果,當然缺點也是有的,就是沒有資源使用率統計,需自行在被測伺服器中監控。
先寫幾行簡單的代碼,寫一個打開BestTest首頁的腳本。
from locust import HttpLocust, TaskSet, task
HttpLocust 這個類的作用是用來發送http請求的
TaskSet 這個類是定義使用者行為的,相當于loadrunnerhttp協定的腳本,jmeter裡面的http請求一樣,要去幹嘛的
task 這個task是一個裝飾器,它用來把一個函數,裝飾成一個任務,也可以指定他們的先後執行順序
class BestTest(TaskSet):
自己定義的類,繼承TaskSet,也就是這個類是實作咱們要去請求什麼的
@task#用task裝飾器把這個函數裝飾成一個咱們要執行的性能任務
def index(self):#這個函數裡面定義的是咱們要具體做的操作
self.client.get(\'/\')#請求這個url裡面的哪個路徑,如果是接口的話,就是哪個接口
class BestTestIndexUser(HttpLocust):
這個類繼承了HttpLocust,代表每個并發裡面的每個使用者
task_set = BestTest #這個是每個使用者都去幹什麼,指定了BestTest這個類,它就會每個使用者去運作besttest這個類裡面的方法
if name == "main":
import os
os.system("locust -f ./load_test.py --host=https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew")
或者注釋掉程式入口代碼,直接指令行執行下面
locust -f besttest.py --host=https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew
-f是指定一個python檔案 後面跟上咱們剛才寫的python檔案
--host是你要通路哪個背景,後面跟的url
預設端口号8090,啟動locust:運作完之後,通路的時候用ip:8090就可以通路了.

locust控制台頁面:
開始測試後的頁面:
tps圖和響應時間圖:
剛才上面寫的例子是單個接口壓測, 或者更說單場景的,如果想做混合場景的壓測。
隻需要寫多個task就可以了,也就是在類裡面寫多個函數,想誰想執行,标上數字就行了,1,2,3,4代碼如下:
不同類型的模拟使用者
1.考慮一種測試場景:如果我們想測試前端使用者登入和詳情,需要并發100個人,并且使其中10個人登入,另外90個人在詳情頁瞎逛,用locust架構應該如何實作呢?架構的指令行有提示locust [options] [LocustClass [LocustClass2 ...]]
當我們使用架構運作一個測試腳本時,如果腳本中隻有一個Locust類,則架構會以這個類作為模拟使用者的模闆;如果該腳本中不止一個Locust類,則需要指定類名;如果需要模拟多種類型的使用者,則可以指定多個類名,架構會根據每個Locust類的權重配置設定模拟使用者的比例(預設權重為10)。
一個簡單示例如下:
from locust import TaskSet, HttpLocust, task, TaskSequence, seq_task, InterruptTaskSet
class LoginTaskSet(TaskSet):
@task
def login(self):
print("登入")
class DetailsTaskSet(TaskSet):
@task
def details(self):
print("詳情頁")
class UserOwn(HttpLocust):
weight = 10
task_set = LoginTaskSet
host = "https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew"
min_wait = 10000
max_wait = 10000
class UserTwo(HttpLocust):
weight = 90
task_set = DetailsTaskSet
host = "https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew"
min_wait = 10000
max_wait = 10000
指令行運作:locust -f ***.py UserOwn UserTwo
如果并發10人,就會有1人在登入,9人在詳情頁瞎逛了,業務是自己編的
2.TaskSet的tasks屬性
定義任務的方式,就是在TaskSet類中定義一個方法,并标注其為@task;另一個定義任務的方式就是使用TaskSet的tasks屬性,這個屬性值可以是數組或字典,其中表示的任務可以是一個函數,也可以是另一個TaskSet。如果使用數組,其元素可以是元組(callable, int),int值即為該任務的權重。
2.1函數
示例中的tasks = [(play_one, 5), play_two]與tasks = {play_one: 5, play_two: 1}是完全等價的。tasks中的函數和有注解@task的函數有同等的地位,都是這個TaskSet的一個基本機關。
函數需有一個參數表示self
def test_one(self):
print("test_one")
def test_two(self):
print("test_two")
class TestTaskSet(TaskSet):
tasks = [(test_one, 5), test_two]
@task
def test(self):
print("the test is me")
class TestTwo(HttpLocust):
weight = 90
task_set = TestTaskSet
host = "https://mp.weixin.qq.com/s/rhjKkj7pKpLX0YtbKOZxew"
min_wait = 100
max_wait = 100
2.2嵌套TaskSet
tasks屬性的元素也可以是另一個TaskSet,這種做法就會形成嵌套的子任務。例如:我們測試前端,需要每一個使用者在詳情頁瞎逛的時候,有1/10的機率進入詳情頁進行另外的操作,詳情頁逛完退回首頁繼續閑逛,示例如下:
class TestTaskSet(TaskSet):
tasks = [test_one, test_two]
@task(5)
def test_details(self):
print("the details is Wandering")
@task
def end_details(self):
print("details end")
self.interrupt()
class TestOutTaskSet(TaskSet):
tasks = {TestTaskSet: 1}
@task(9)
def Test_out(self):
print("the details is out")
當進入嵌套的任務集執行任務後,會根據嵌套任務集的任務權重執行任務,除非調用self.interrupt()或抛出中斷異常raise InterruptTaskSet否則将一直在子任務集中執行下去
3.内部類
還有一種實作嵌套子任務的方式是使用内部類,如上訴示例,等同于:
class TestOutTaskSet(TaskSet):
@task(9)
def Test_out(self):
print("the details is out")
@task
class TestTaskSet(TaskSet):
tasks = [test_one, test_two]
@task(5)
def test_details(self):
print("the details is Wandering")
@task
def end_details(self):
print("details end")
self.interrupt()
4.類的繼承
如果一個TaskSet的父類是另一個TaskSet,那麼父類的所有任務會同樣的成為子類的任務。
5.最後尾聲
Locust類是建立模拟使用者的模闆,是以如果我們要使用不同的角色進行測試時,就需要給架構提供不同的Locust類。TaskSet類是模拟需要執行的任務集,我覺得可以這麼了解,這是一個任務的池子,裡面的每個機關都是一個任務,這個任務可以是函數也可以是另一個TaskSet,這個任務的定義方式有幾個(目前TaskSet通過@task定義,繼承父類的任務,tasks屬性中一個元素),但它們都是同等地位的,執行的機率與它們的權重相同,每一次模拟使用者都會從這個池子裡挑選任務進行執行(直到stop_timeout或頁面點選Stop)。當挑選到一個類型為TaskSet的任務執行時,就進入了嵌套的子任務當中,相當于到了子任務的池子裡開始挑選任務執行(直到調用self.interrupt()或抛出中斷異常raise InterruptTaskSet)。