簡介
Locust是一個使用Python編寫的可擴展、分布式的開源性能測試工具。
優點
- 相比于Jmeter、LoadRunner這種基于GUI的工具而言,Locust使用Python語言來描述測試場景使模擬用戶行為變得更加靈活和簡潔,除了Http(s)協議之外,Locust可以測試任意協議的系統,只需要實現Python調用對應協議的庫進行請求即可(類似HttpLocust類)。
- Locust的并發機制采用協程的方式,相比于進程和線程減少了系統級資源調度,因此單機的產生的并發能力相比于LoadRunner、jmeter得到了大幅的提升。
安裝
pip install locust
Locust腳本編寫
import queue
from locust import HttpLocust, TaskSet, task
from locust.clients import HttpSession
from sign import ParserData
class UserBehavior(TaskSet):
parser_data = ParserData() # 解析接口傳參類
_client = 20
version = 119
user_info = None
@staticmethod
def get_user_info(response):
r = response.json().get('content')
return {
'market_id': r.get('marketId'),
'token': r.get('token')
}
def on_start(self):
try:
user, password = self.locust.users.get_nowait()
except queue.Empty:
print('test data run out. test ended.')
exit(0)
client = HttpSession(base_url='http://login.xxxx.cn')
data = self.parser_data(loginName=user,
password=password,
client=self._client,
version=self.version)
response = client.post(url='/login', data=data)
self.user_info = self.get_user_info(response)
self.locust.users.put_nowait((user, password))
@task(2)
def index(self):
url = '/index'
data = self.parser_data(market_id=self.user_info['market_id'],
client=self._client,
version=self.version,
pnum='3')
headers = {'Authorization': 'Barer:' + self.user_info['token'],
'Accept': 'application/vnd.hs-api.v1+json'}
with self.client.post(url=url, data=data, headers=headers,
verify=False, catch_response=True) as response:
if response.status_code == 200:
response.success()
else:
response.failure('http error.')
@task(1)
def shop_car_list(self):
url = '/shopCar/list'
data = self.parser_data(market_id=self.user_info['market_id'],
ischaidan='1',
client=self._client,
version=self.version)
headers = {'Authorization': 'Bearer:' + self.user_info['token'],
'Accept': 'application/vnd.hs-api.v1+json'}
with self.client.post(name='ShopCar', url=url, data=data, headers=headers,
verify=False, catch_response=True) as response:
if response.status_code != 200 or "失敗" in response.text:
response.failure('response error.')
else:
response.success()
class Stay(TaskSet):
index = 0
def on_start(self):
self.index += 1
@task
def get_error(self):
response = self.client.get('/1', name='error', allow_redirects=False,
verify=False, catch_response=True)
if response.status_code == 200:
response.success()
else:
response.failure('http error.')
@task
def logout(self):
self.interrupt()
class User(TaskSet):
tasks = {Stay: 1}
@task(1)
def user(self):
self.client.get('/', verify=False)
class WebsiteUser(HttpLocust):
task_set = UserBehavior
host = 'https://xxxx.api.xxxx.cn'
min_wait = 1000
max_wait = 3000
users = queue.Queue()
users.put_nowait(('user1', '1232'))
users.put_nowait(('user2', '1234'))
users.put_nowait(('user3', '1321'))
weight = 3
stop_timeout = 20
class WebsiteU(HttpLocust):
task_set = User
host = 'https://www.baidu.com'
min_wait = 0
max_wait = 0
weight = 1
stop_timeout = 60
簡單解釋下:
- UserBehavior和WebsiteUser兩個類實現測試場景使用3個用戶賬號,每個用戶會去先登錄,然后分別去查看首頁和進入購物車頁面
- 首先導入了需要用到的類,HttpLocust類為Locust子類,模擬客戶端的請求類,Taskset類為任務集類,task為任務裝飾器。
Taskset
UserBehavior為Taskset子類,該類主要用來定義每個虛擬用戶的操作行為
Taskset子類中可以定義一個on_start方法在正式開始測試前只執行一次,相當與初始化操作(這里說的只執行一次是每個虛擬用戶都會去執行一次),比如獲取登錄token等操作。
teskset類中的每個任務都需要用@task(weight=1)裝飾器去裝飾為一個任務,weight為執行的權重,如果不裝飾,locust不會認為這是一個任務,UserBehavior類中@task(1)、@task(2)裝飾器表示3個用戶里面有2個用戶去模擬執行index方法,有1個用戶去執行shop_car_list方法
taskset類中self.client屬性請求操作時傳入catch_response參數,設置為True,可以標記響應結果為成功或失敗,即使響應是成功的,也可以標記為失敗,默認為Fasle
taskset類中self.client屬性請求操作時有一個name參數,當設置了name的值時,最后的請求結果展示中name字段會顯示這里定義的name值,相當與給這個方法起了一個別名.
taskset類中interrupt(reschedule=True)方法在頂層的taskset類(即被指定到Locust子類中的taskset)中不可用,reschedule為True時,從被嵌套的任務中出來立即執行新的任務,如果為False從被嵌套的任務中出來會等待min_time-max_time之間的隨機時間,然后再執行新的任務,這個方法主要用來跳出嵌套的任務集
HttpLocust
WebsiteUser為HttpLocust子類,該類是用來模擬用戶的類,定義了一些用戶信息,及請求方式
HttpLocust子類中task_set屬性用來指定模擬用戶執行的操作,即Taskset子類
HttpLocust子類中的host屬性為被測試系統的host,當命令行中沒有指定--host參數時,此屬性會生效
HttpLocust子類中的min_wait、max_weight為最大等待時間和最小等待時間,每個請求會從這兩個時間間隔中隨機取一個時間等待,相當用戶實際操作系統時每個動作的思考時間。若測試單個接口則對應的值都設置為0即可。如果Taskset類中定義了min_wait、max_weight則會覆蓋Locust子類中定義的值。單位ms,默認值1000ms
HttpLocust子類中的stop_timeout屬性為執行測試的時間,單位為s
HttpLocust子類中的weight為該類執行的權重,當有多個子類時生效,如WebsiteUser、WebsiteU兩個HttpLocust子類中weight值分別為3和1.
Locust默認單機單進程運行,此模式下并不能充分利用單機的多處理器,可使用分布式運行,即開啟一個master,n個slave(n為處理器個數),master負責啟動Locust的web服務和任務分發,不會產生壓力,slave主要負責產生壓力
運行模式:
no-web模式
no_web模式指在命令行中直接運行
locust -f load_test.py -c 1 -r 1 -n 1
- -f 指定要運行的Locust性能測試文件
- -c 指定模擬的并發用戶數
- -r 指定每秒的啟動用戶數
- -n 指定運行次數
- -t 指定運行的時間,例如300s,1m,1h
寫完腳本調試時可在該模式下運行
單機單進程運行
locust -f load_test.py
分布式運行
分布式運行,有單機多進程運行和多機多進程運行兩種
locust -f load_test.py --master master模式下啟動locust
locust -f load_test.py --slave 啟動一個locust slave節點,單機多進程模式
locust -f load_test.py --slave --master-host=192.168.105.11 啟動一個locust slave節點,多機模式下
no_web模式下運行
web模式
- Number of users to simulate:需要模擬的虛擬用戶個數
-
Hatch rate (users spawned/second):啟動虛擬用戶的速率,每秒產生出多少個用戶數
QQ圖片20171109190334.png - 顯示并發數、響應時間、異常率、每秒請求數等
-
reqs/sec(每秒請求數)為根據最近2s請求數據計算得到的數據,即瞬時值
QQ圖片20171109190430.png -
顯示rps、響應時間、并發數在整個測試運行中的走勢圖
QQ圖片20171109190455.png - 顯示測試過程中出現的所有失敗的請求
Exceptions顯示測試過程中出現的異常
Download Date提供測試結果csv文件的下載
測試數據:
- locust子類中設置的數據是全局的,為list時,使用自增方式取數據,并發運行時會出現取到的數據重復的情況,如果對數據唯一性有要求,使用python的queue隊列的數據結構即可
- taskset子類中設置的數據是局部的,即每一個虛擬用戶都會有一個屬于自己的這個變量
queue數據結構
隊列形式
q=queue.Queue(maxsize=3) 先進先出隊列
q.put(1) 向隊列中存數據
q.get() 向隊列中取數據
q.put_nowait() 相當于q.put(1, block=False),當q隊列滿了之后put會觸發queue.Full異常
q.get_nowait() 相當與q.get(block=False),當q隊列為空之后get會觸發queue.Empty異常