性能測試工具Locust的介紹和使用

內容來自網絡html

https://www.w3xue.com/exp/article/20191/16707.htmlnode

https://blog.csdn.net/qq_36255988/article/details/82622044python

1、Locust描述nginx

(1)git

locust是一個易於使用的,分佈式的,用戶負載測試工具。用於web站點(或其餘系統)的負載測試,而後算出系統可以處理多少併發用戶。
locust的思想是:在測試期間,一大羣"蝗蟲"會攻擊你的網站,每個"蝗蟲"的行爲都是由你本身定義的,同時,能夠在一個web界面上實時的監控這羣進程。這會幫助你更好的"進行戰鬥",在真正的用戶進入以前,就找出代碼中的瓶頸。
locust徹底是事件驅動的,所以它可以在單機支持數以千計的併發用戶,相比許多其餘的基於事件的應用,locust不使用回調函數。它使用輕量進程---gevent。每個訪問你的網站的locust實際上都在它本身的進程內部運行(準確地說,是greenlet),也就是咱們一般說的協程。這容許你在不使用帶回調函數的複雜代碼的情形下,使用python寫出很是具備表現力的腳本。
(2)
Locust是開源、使用Python開發、基於事件、支持分佈式而且提供Web UI進行測試執行和結果展現的性能測試工具。而它之因此可以在資源佔用方面明顯優於JMeter,一個關鍵點在於二者模擬虛擬用戶的方式不一樣,JMeter經過線程來做爲虛擬用戶,而Locust藉助gevent庫對協程的支持,以greenlet來實現對用戶的模擬,相同配置下Locust能支持的併發用戶數相比JMeter能夠達到一個數量級的提高。
Locust使用Python代碼定義測試場景,目前支持Python 2.7, 3.3, 3.4, 3.5, 3.6和3.7。它自帶一個Web UI,用於定義用戶模型,發起測試,實時測試數據,錯誤統計等。
當前最新發布版本v0.11.1,還提供QPS、評價響應時間等幾個簡單的圖表。

優勢:github

1. 易用。很方便地基於Python進行腳本擴展和業務請求實現。golang

2. 徹底基於事件驅動,因此不受進程和線程的限制,能夠支持發起更高的併發數請求。web

3. 能夠分佈式發起併發請求算法

4.Locust有一個整潔的HTML+JS的用戶界面,實時顯示相關測試細節。因爲用戶界面是基於網絡的,它是跨平臺的和容易擴展。json

5. 開源。

缺點:

1. 圖表相對loadrunner 比較簡單。(在Linux 下部署時能夠看到圖表,在Windows 下沒有)

2. 不支持監控被測機,須要結合nmon等工具輔助監控。

2、Locust安裝

1.一、   --->  pip3 install locust 

1.2 、 經過GitHub上克隆項目安裝(Python3推薦):https://github.com/locustio/locust  ,而後執行     ...\locust> python setup.py install

 二、安裝 pyzmq

 If you intend to run Locust distributed across multiple processes/machines, we recommend you to also install pyzmq.

 若是打算運行Locust 分佈在多個進程/機器,須要安裝pyzmq.

 經過pip命令安裝。 />  pip install pyzmq

三、安裝成功,CMD敲入命令驗證。 /> locust --help

 1 Options:
 2   -h, --help            show this help message and exit
 3   -H HOST, --host=HOST  Host to load test in the following format:
 4                         http://10.21.32.33
 5   --web-host=WEB_HOST   Host to bind the web interface to. Defaults to '' (all
 6                         interfaces)
 7   -P PORT, --port=PORT, --web-port=PORT
 8                         Port on which to run web host
 9   -f LOCUSTFILE, --locustfile=LOCUSTFILE
10                         Python module file to import, e.g. '../other.py'.
11                         Default: locustfile
12   --csv=CSVFILEBASE, --csv-base-name=CSVFILEBASE
13                         Store current request stats to files in CSV format.
14   --master              Set locust to run in distributed mode with this
15                         process as master
16   --slave               Set locust to run in distributed mode with this
17                         process as slave
18   --master-host=MASTER_HOST
19                         Host or IP address of locust master for distributed
20                         load testing. Only used when running with --slave.
21                         Defaults to 127.0.0.1.
22   --master-port=MASTER_PORT
23                         The port to connect to that is used by the locust
24                         master for distributed load testing. Only used when
25                         running with --slave. Defaults to 5557. Note that
26                         slaves will also connect to the master node on this
27                         port + 1.
28   --master-bind-host=MASTER_BIND_HOST
29                         Interfaces (hostname, ip) that locust master should
30                         bind to. Only used when running with --master.
31                         Defaults to * (all available interfaces).
32   --master-bind-port=MASTER_BIND_PORT
33                         Port that locust master should bind to. Only used when
34                         running with --master. Defaults to 5557. Note that
35                         Locust will also use this port + 1, so by default the
36                         master node will bind to 5557 and 5558.
37   --heartbeat-liveness=HEARTBEAT_LIVENESS
38                         set number of seconds before failed heartbeat from
39                         slave
40   --heartbeat-interval=HEARTBEAT_INTERVAL
41                         set number of seconds delay between slave heartbeats
42                         to master
43   --expect-slaves=EXPECT_SLAVES
44                         How many slaves master should expect to connect before
45                         starting the test (only when --no-web used).
46   --no-web              Disable the web interface, and instead start running
47                         the test immediately. Requires -c and -r to be
48                         specified.
49   -c NUM_CLIENTS, --clients=NUM_CLIENTS
50                         Number of concurrent Locust users. Only used together
51                         with --no-web
52   -r HATCH_RATE, --hatch-rate=HATCH_RATE
53                         The rate per second in which clients are spawned. Only
54                         used together with --no-web
55   -t RUN_TIME, --run-time=RUN_TIME
56                         Stop after the specified amount of time, e.g. (300s,
57                         20m, 3h, 1h30m, etc.). Only used together with --no-
58                         web
59   -L LOGLEVEL, --loglevel=LOGLEVEL
60                         Choose between DEBUG/INFO/WARNING/ERROR/CRITICAL.
61                         Default is INFO.
62   --logfile=LOGFILE     Path to log file. If not set, log will go to
63                         stdout/stderr
64   --print-stats         Print stats in the console
65   --only-summary        Only print the summary stats
66   --no-reset-stats      [DEPRECATED] Do not reset statistics once hatching has
67                         been completed. This is now the default behavior. See
68                         --reset-stats to disable
69   --reset-stats         Reset statistics once hatching has been completed.
70                         Should be set on both master and slaves when running
71                         in distributed mode
72   -l, --list            Show list of possible locust classes and exit
73   --show-task-ratio     print table of the locust classes' task execution
74                         ratio
75   --show-task-ratio-json
76                         print json data of the locust classes' task execution
77                         ratio
78   -V, --version         show program's version number and exit

參數說明:

  1. -h, --help 查看幫助
  2. -H HOST, --host=HOST 指定被測試的主機,採用以格式:http://10.21.32.33
  3. --web-host=WEB_HOST 指定運行 Locust Web 頁面的主機,默認爲空 ''
  4. -P PORT, --port=PORT, --web-port=PORT 指定 --web-host 的端口,默認是8089
  5. -f LOCUSTFILE, --locustfile=LOCUSTFILE 指定運行 Locust 性能測試文件,默認爲: locustfile.py
  6. --csv=CSVFILEBASE, --csv-base-name=CSVFILEBASE CSV格式存儲當前請求測試數據。
  7. --master Locust 分佈式模式使用,當前節點爲 master 節點。
  8. --slave Locust 分佈式模式使用,當前節點爲 slave 節點。
  9. --master-host=MASTER_HOST 分佈式模式運行,設置 master 節點的主機或 IP 地址,只在與 --slave 節點一塊兒運行時使用,默認爲:127.0.0.1.
  10. --master-port=MASTER_PORT 分佈式模式運行, 設置 master 節點的端口號,只在與 --slave 節點一塊兒運行時使用,默認爲:5557。注意,slave 節點也將鏈接到這個端口+1 上的 master 節點。
  11. --master-bind-host=MASTER_BIND_HOST Interfaces (hostname, ip) that locust master should bind to. Only used when running with --master. Defaults to * (all available interfaces).
  12. --master-bind-port=MASTER_BIND_PORT Port that locust master should bind to. Only used when running with --master. Defaults to 5557. Note that Locust will also use this port + 1, so by default the master node will bind to 5557 and 5558.
  13. --expect-slaves=EXPECT_SLAVES How many slaves master should expect to connect before starting the test (only when --no-web used).
  14. --no-web no-web 模式運行測試,須要 -c -r 配合使用.
  15. -c NUM_CLIENTS, --clients=NUM_CLIENTS 指定併發用戶數,做用於 --no-web 模式。
  16. -r HATCH_RATE, --hatch-rate=HATCH_RATE 指定每秒啓動的用戶數,做用於 --no-web 模式。
  17. -t RUN_TIME, --run-time=RUN_TIME 設置運行時間, 例如: (300s, 20m, 3h, 1h30m). 做用於 --no-web 模式。
  18. -L LOGLEVEL, --loglevel=LOGLEVEL 選擇 log 級別(DEBUG/INFO/WARNING/ERROR/CRITICAL). 默認是 INFO.
  19. --logfile=LOGFILE 日誌文件路徑。若是沒有設置,日誌將去 stdout/stderr
  20. --print-stats 在控制檯中打印數據
  21. --only-summary 只打印摘要統計
  22. --no-reset-stats Do not reset statistics once hatching has been completed
  23. -l, --list 顯示測試類, 配置 -f 參數使用
  24. --show-task-ratio 打印 locust 測試類的任務執行比例,配合 -f 參數使用.
  25. --show-task-ratio-json json 格式打印 locust 測試類的任務執行比例,配合 -f 參數使用.
  26. -V, --version 查看當前 Locust 工具的版本.

四、Locust主要由下面的幾個庫構成:

1) gevent

gevent是一種基於協程的Python網絡庫,它用到Greenlet提供的,封裝了libevent事件循環的高層同步API。

2) flask

Python編寫的輕量級Web應用框架。

3) requests

Python Http庫

4) msgpack-python

MessagePack是一種快速、緊湊的二進制序列化格式,適用於相似JSON的數據格式。msgpack-python主要提供MessagePack數據序列化及反序列化的方法。

5) six

Python2和3兼容庫,用來封裝Python2和Python3之間的差別性

6) pyzmq

pyzmq是zeromq(一種通訊隊列)的Python綁定,主要用來實現Locust的分佈式模式運行

當咱們在安裝 Locust 時,它會檢測咱們當前的 Python 環境是否已經安裝了這些庫,若是沒有安裝,它會先把這些庫一一裝上。而且對這些庫版本有要求,有些是必須等於某版本,有些是大於某版本。咱們也能夠事先把這些庫所有按要求裝好,再安裝Locust時就會快上許多。

3、編寫接口壓測腳本文件locustfile.py

腳本模板(參考)

 1 from locust import HttpLocust, TaskSet, task
 2 
 3 class UserBehavior(TaskSet):
 4     def setup(self):
 5         print('task setup')
 6 
 7     def teardown(self):
 8         print('task teardown')
 9 
10     def on_start(self):
11         # 虛擬用戶啓動Task時運行
12         print('start')
13 
14     def on_stop(self):
15         # 虛擬用戶結束Task時運行
16         print('end')
17 
18     @task(2)
19     def index(self):
20         self.client.get("/")
21 
22     @task(1)
23     def profile(self):
24         self.client.get("/profile")
25 
26 class WebsiteUser(HttpLocust):
27     def setup(self):
28         print('locust setup')
29 
30     def teardown(self):
31         print('locust teardown')
32 
33     host = http: // XXXXX.com
34     task_set = UserBehavior
35     min_wait = 5000
36     max_wait = 9000
37 
38 if __name__ == '__main__':
39     pass

說明:

Locust類有setup和teardown,TaskSet類有setup、teardown、on_start、on_stop。

每次啓動locust時運行setup方法,退出時運行teardown方法,locust執行TaskSet時運行TaskSet的setup方法,退出時運行teardown方法,每一個虛擬用戶執行操做時運行on_start方法,退出時執行on_stop方法,運行上面的腳本,執行順序以下:

執行順序:Locust setup → TaskSet setup → TaskSet on_start → TaskSet tasks → TaskSet on_stop → TaskSet teardown → Locust teardown

舉個腳本栗子

 1 from locust import HttpLocust, TaskSet, task
 2 
 3 
 4 class ScriptTasks(TaskSet):
 5     def on_start(self):
 6         self.client.post("/login", {
 7             "username": "test",
 8             "password": "123456"
 9         })
10 
11     @task(2)
12     def index(self):
13         self.client.get("/")
14 
15     @task(1)
16     def about(self):
17         self.client.get("/about/")
18 
19     @task(1)
20     def demo(self):
21         payload = {}
22         headers = {}
23         self.client.post("/demo/", data=payload, headers=headers)
24 
25 
26 class WebsiteUser(HttpLocust):
27     task_set = ScriptTasks
28     host = "http://example.com"
29     min_wait = 1000
30     max_wait = 5000

腳本解讀

  1. 建立ScriptTasks()類繼承TaskSet類: 用戶行爲類,用於定義測試業務場景。
  2. 建立index()、about()、demo()方法分別表示一個行爲,訪問http://example.com。用@task() 裝飾該方法爲一個任務。一、2表示一個Locust實例被挑選執行的權重,數值越大,執行頻率越高。在當前ScriptTasks()行爲下的三個方法得執行比例爲2:1:1
  3. WebsiteUser()類: 用於定義模擬用戶。
  4. task_set 指向一個定義了的用戶行爲類。
  5. host 指定被測試應用的URL的地址
  6. min_wait 用戶執行任務之間等待時間的下界,單位:毫秒。
  7. max_wait 用戶執行任務之間等待時間的上界,單位:毫秒。

腳本使用場景解讀

一、在這個示例中,定義了針對http://example.com網站的測試場景:先模擬用戶登陸系統,而後隨機地訪問首頁(/)和關於頁面(/about/),請求比例爲2:1,demo方法主要用來闡述client對post接口的處理方式;而且,在測試過程當中,兩次請求的間隔時間爲1->5秒間的隨機值。

二、從腳本中能夠看出,腳本主要包含兩個類(類名可自定義),一個是WebsiteUser(繼承自HttpLocust,而HttpLocust繼承自Locust),另外一個是ScriptTasks(繼承自TaskSet)。事實上,在Locust的測試腳本中,全部業務測試場景都是在LocustTaskSet兩個類的繼承子類中進行描的。

三、那如何理解LocustTaskSet這兩個類呢?簡單地說,Locust類就比如是一羣蝗蟲,而每一隻蝗蟲就是一個類的實例。相應的,TaskSet類就比如是蝗蟲的大腦,控制着蝗蟲的具體行爲,即實際業務場景測試對應的任務集。

4、Locust類

實例腳本

僞代碼:

 1 from locust import HttpLocust, TaskSet, task
 2 
 3 
 4 class WebsiteTasks(TaskSet):
 5     def on_start(self):  # 進行初始化的工做,每一個Locust用戶開始作的第一件事
 6         payload = {
 7             "username": "test_user",
 8             "password": "123456",
 9         }
10         header = {
11             "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
12         }
13         self.client.post("/login", data=payload,headers=header)  
14         # self.client屬性使用Python request庫的全部方法,調用和使用方法和requests徹底一致;
15 
16     @task(5)  
17     # 經過@task()裝飾的方法爲一個事務,方法的參數用於指定該行爲的執行權重,參數越大每次被虛擬用戶執行的機率越高,默認爲1
18     def index(self):
19         self.client.get("/")
20 
21     @task(1)
22     def about(self):
23         self.client.get("/about/")
24 
25 
26 class WebsiteUser(HttpLocust):
27     host = "https://github.com/"  # 被測系統的host,在終端中啓動locust時沒有指定--host參數時纔會用到
28     task_set = WebsiteTasks  # TaskSet類,該類定義用戶任務信息,必填。這裏就是:WebsiteTasks類名,由於該類繼承TaskSet;
29     min_wait = 5000  # 每一個用戶執行兩個任務間隔時間的上下限(毫秒),具體數值在上下限中隨機取值,若不指定默認間隔時間固定爲1秒
30     max_wait = 15000

 僞代碼中對https://github.com/網站的測試場景,先模擬用戶登陸系統,而後隨機訪問首頁/和/about/,請求比例5:1,而且在測試過程當中,兩次請求的間隔時間1-5秒的隨機值;

 on_start方法,在正式執行測試前執行一次,主要用於完成一些初始化的工做,例如登陸操做;

WebsiteTasks類中如何去調用 WebsiteUser(HttpLocust)類中定義的字段和方法呢?

 經過在WebsiteTasks類中self.locust.xxoo      xxoo就是咱們在WebsiteUser類中定義的字段或方法;

僞代碼:

 1 from locust import HttpLocust, TaskSet, task
 2 import hashlib
 3 import Queue
 4 
 5 
 6 class WebsiteTasks(TaskSet):
 7     @task(5)
 8     def index(self):
 9         data = self.locust.user_data_queue  # 獲取WebsiteUser裏面定義的ser_data_queue隊列
10         md5_data = self.locust.md5_encryption()  # 獲取WebsiteUser裏面定義的md5_encryption()方法
11         self.client.get("/")
12 
13 
14 class WebsiteUser(HttpLocust):
15     host = "https://github.com/"
16     task_set = WebsiteTasks
17     min_wait = 5000
18     max_wait = 15000
19     user_data_queue = Queue.Queue()
20 
21     def md5_encryption(self, star):
22         '''md5加密方法'''
23         obj = hashlib.md5()
24         obj.update(bytes(star, encoding="utf-8"))
25         result = obj.hexdigest()
26         return result

僞代碼中測試場景如何表達?

代碼主要包含兩個類:

  1. WebsiteUser繼承(HttpLocust,而HttpLocust繼承自Locust)
  2. WebsiteTasks繼承(TaskSet)

在Locust測試腳本中,全部業務測試場景都是在Locust和TaskSet兩個類的繼承子類中進行描述;

簡單說:Locust類就相似一羣蝗蟲,而每隻蝗蟲就是一個類的實例。TaskSet類就相似蝗蟲的大腦,控制蝗蟲的具體行爲,即實際業務場景測試對應的任務集;

源碼中:class Locust(object)和class HttpLocust(Locust) 此處可查看源代碼

在Locust類中,靜態字段client即客戶端的請求方法,這裏的client字段沒有綁定客戶端請求方法,所以在使用Locust時,須要先繼承Locust類class HttpLocust(Locust),而後在self.client =HttpSession(base_url=self.host)綁定客戶端請求方法;

對於常見的HTTP(s)協議,Locust已經實現了HttpLocust類,其self.client=HttpSession(base_url=self.host),而HttpSession繼承自requests.Session。所以在測試HTTP(s)的Locust腳本中,能夠經過client屬性來使用Python requests庫的所 有方法,調用方式與      reqeusts徹底一致。另外,因爲requests.Session的使用,client的方法調用之間就自動具備了狀態記憶功能。常見的場景就是,在登陸系統後能夠維持登陸狀態的Session,從然後續HTTP請求操做都能帶上登陸狀態;

Locust類中,除了client屬性,還有幾個屬性須要關注:

  • task_set ---> 指向一個TaskSet類,TaskSet類定義了用戶的任務信息,該靜態字段爲必填;
  • max_wait/min_wait ---> 每一個用戶執行兩個任務間隔的上下限(毫秒),具體數值在上下限中隨機取值,若不指定則默認間隔時間爲1秒;
  • host    --->被測試系統的host,當在終端中啓動locust時沒有指定--host參數時纔會用到;
  • weight--->同時運行多個Locust類時,用於控制不一樣類型的任務執行權重;

Locust流程,測試開始後,每一個虛擬用戶(Locust實例)運行邏輯都會遵照以下規律:

  1. 先執行WebsiteTasks中的on_start(只執行一次),做爲初始化;
  2. 從WebsiteTasks中隨機挑選(若是定義了任務間的權重關係,那麼就按照權重關係隨機挑選)一個任務執行;
  3. 根據Locust類中min_wait和max_wait定義的間隔時間範圍(若是TaskSet類中也定義了min_wait或者max_wait,以TaskSet中的優先),在時間範圍中隨機取一個值,休眠等待;
  4. 重複2~3步驟,直到測試任務終止;

class TaskSet

TaskSet類實現了虛擬用戶所執行任務的調度算法,包括規劃任務執行順序(schedule_task)、挑選下一個任務(execute_next_task)、執行任務(execute_task)、休眠等待(wait)、中斷控制(interrupt)等待。在此基礎上,就能夠在TaskSet子類中採用很是簡潔的方式來描述虛擬用戶的業務測試場景,對虛擬用戶的全部行爲進行組織和描述,並能夠對不一樣任務的權重進行配置。

@task

經過@task()裝飾的方法爲一個事務。方法的參數用於指定該行爲的執行權重。參數越大每次被虛擬用戶執行的機率越高。若是不設置默認爲1。

TaskSet子類中定義任務信息時,採起兩種方式:@task裝飾器和tasks屬性。

採用@task裝飾器定義任務信息時:

 1 from locust import TaskSet, task
 2 
 3 class UserBehavior(TaskSet):
 4     @task(1)
 5     def test_job1(self):
 6         self.client.get('/test1')
 7 
 8     @task(3)
 9     def test_job2(self):
10         self.client.get('/test2')

 

採用tasks屬性定義任務信息時

 1 from locust import TaskSet
 2 
 3 def test_job1(obj):
 4     obj.client.get('/test1')
 5 
 6 def test_job2(obj):
 7     obj.client.get('/test2')
 8 
 9 class UserBehavior(TaskSet):
10     tasks = {test_job1: 1, test_job2: 2}
11     # tasks = [(test_job1,1), (test_job1,3)] # 兩種方式等價

上面兩種定義任務信息方式中,均設置了權重屬性,即執行test_job2的頻率是test_job1的兩倍。若不指定,默認比例爲1:1。

高級用法:

關聯

在某些請求中,須要攜帶以前response中提取的參數,常見場景就是session_id。Python中可用經過re正則匹配,對於返回的html頁面,可用採用lxml庫來定位獲取須要的參數;

 1 from locust import HttpLocust, TaskSet, task
 2 from lxml import etree
 3 
 4 class WebsiteTasks(TaskSet):
 5     def get_session(self, html):  # 關聯例子
 6         tages = etree.HTML(html)
 7         return tages.xpath("//div[@class='btnbox']/input[@name='session']/@value")[0]
 8 
 9     def on_start(self):
10         html = self.client.get('/index')
11         session = self.get_session(html.text)
12         payload = {
13             "username": "test_user",
14             "password": "123456",
15             'session': session
16         }
17         header = {
18             "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"19         }
20         self.client.post("/login", data=payload, headers=header)
21 
22     @task(5)
23     def index(self):
24         self.client.get("/")
25         assert response['ErrorCode'] == 0  # 斷言
26 
27     @task(1)
28     def about(self):
29         self.client.get("/about/")
30 
31 class WebsiteUser(HttpLocust):
32     host = "https://github.com/"
33     task_set = WebsiteTasks
34     min_wait = 5000
35     max_wait = 15000

參數化

做用:循環取數據,數據可重複使用

 例如:模擬3個用戶併發請求網頁,共有100個URL地址,每一個虛擬用戶都會依次循環加載100個URL地址

 1 from locust import TaskSet, task, HttpLocust
 2 
 3 class UserBehavior(TaskSet):
 4     def on_start(self):
 5         self.index = 0
 6 
 7     @task
 8     def test_visit(self):
 9         url = self.locust.share_data[self.index]
10         print('visit url: %s' % url)
11         self.index = (self.index + 1) % len(self.locust.share_data)
12         self.client.get(url)
13 
14 class WebsiteUser(HttpLocust):
15     host = 'http://debugtalk.com'
16     task_set = UserBehavior
17     share_data = ['url1', 'url2', 'url3', 'url4', 'url5']
18     min_wait = 1000
19     max_wait = 3000

 保證併發測試數據惟一性,不循環取數據;

 全部併發虛擬用戶共享同一份測試數據,而且保證虛擬用戶使用的數據不重複;

例如:模擬3用戶併發註冊帳號,共有9個帳號,要求註冊帳號不重複,註冊完畢後結束測試:

採用隊列

 1 from locust import TaskSet, task, HttpLocust
 2 import Queue
 3 
 4 class UserBehavior(TaskSet):
 5     @task
 6     def test_register(self):
 7         try:
 8             data = self.locust.user_data_queue.get()
 9         except Queue.Empty:
10             print('account data run out, test ended.')
11             exit(0)
12         print('register with user: {}, pwd: {}'.format(data['username'], data['password']))
13         payload = {
14             'username': data['username'],
15             'password': data['password']
16         }
17         self.client.post('/register', data=payload)
18 
19 class WebsiteUser(HttpLocust):
20     host = 'http://XXXXX.com'
21     task_set = UserBehavior
22     user_data_queue = Queue.Queue()
23     for index in range(100):
24         data = {
25             "username": "test%04d" % index,
26             "password": "pwd%04d" % index,
27             "email": "test%04d@debugtalk.test" % index,
28             "phone": "186%08d" % index,
29         }
30         user_data_queue.put_nowait(data)
31     min_wait = 1000
32     max_wait = 3000

保證併發測試數據惟一性,循環取數據;

全部併發虛擬用戶共享同一份測試數據,保證併發虛擬用戶使用的數據不重複,而且數據可循環重複使用;

例如:模擬3個用戶併發登陸帳號,總共有9個帳號,要求併發登陸帳號不相同,但數據可循環使用;

 

 1 class UserBehavior(TaskSet):
 2     @task
 3     def test_register(self):
 4         try:
 5             data = self.locust.user_data_queue.get()
 6         except Queue.Empty:
 7             print('account data run out, test ended')
 8             exit(0)
 9         print('register with user: {0}, pwd: {1}'.format(data['username'], data['password']))
10         payload = {
11             'username': data['username'],
12             'password': data['password']
13         }
14         self.client.post('/register', data=payload)
15         self.locust.user_data_queue.put_nowait(data)
16 
17 class WebsiteUser(HttpLocust):
18     host = 'http://XXXXXX.com'
19     task_set = UserBehavior
20     user_data_queue = Queue.Queue()
21     for index in range(100):
22         data = {
23             "username": "test%04d" % index,
24             "password": "pwd%04d" % index,
25             "email": "test%04d@debugtalk.test" % index,
26             "phone": "186%08d" % index,
27         }
28         user_data_queue.put_nowait(data)
29     min_wait = 1000
30     max_wait = 3000

斷言(即檢查點)

性能測試也須要設置斷言麼? 某些狀況下是須要,好比你在請求一個頁面時,就能夠經過狀態來判斷返回的 HTTP 狀態碼是否是 200。

經過with self.client.get("url地址",catch_response=True) as response的形式;

response.status_code獲取http響應碼進行判斷,失敗後會加到統計錯誤表中;

python自帶的斷言assert失敗後代碼就不會向下走,且失敗後不會被Locust報表統計進去;

默認不寫參數catch_response=False斷言無效,將catch_response=True才生效;

下面例子中:

首先使用python斷言對接口返回值進行判斷(python斷言不經過,代碼就不向下執行,get請求數爲0),經過後對該接口的http響應是否爲200進行判斷;

 1 @task
 2 def all_interface(self):
 3     # 豆瓣圖書api爲例子
 4     with  self.client.get("https://api.douban.com/v2/book/1220562", name="/LhcActivity/GetActConfig",
 5                           catch_response=True) as response:
 6         assert response.json()['rating']['max'] == 10  # python斷言對接口返回值中的max字段進行斷言
 7         if response.status_code == 200:  # 對http響應碼是否200進行判斷
 8             response.success()
 9         else:
10             response.failure("GetActConfig[Failed!]")

5、Locust運行模式

運行Locust時,一般會使用到兩種運行模式:單進程運行多進程分佈式運行

單進程運行模式

Locust全部的虛擬併發用戶均運行在單個Python進程中,具體從使用形式上,又分爲no_webweb兩種形式。該種模式因爲單進程的緣由,並不能徹底發揮壓力機全部處理器的能力,所以主要用於調試腳本和小併發壓測的狀況。

當併發壓力要求較高時,就須要用到Locust的多進程分佈式運行模式。從字面意思上看,你們可能第一反應就是多臺壓力機同時運行,每臺壓力機分擔負載一部分的壓力生成。的確,Locust支持任意多臺壓力機(一主多從)的分佈式運行模式,但這裏說到的多進程分佈式運行模式還有另一種狀況,就是在同一臺壓力機上開啓多個slave的狀況。這是由於當前階段大多數計算機的CPU都是多處理器(multiple processor cores),單進程運行模式下只能用到一個處理器的能力,而經過在一臺壓力機上運行多個slave,就能調用多個處理器的能力了。比較好的作法是,若是一臺壓力機有N個處理器內核,那麼就在這臺壓力機上啓動一個masterNslave。固然,咱們也能夠啓動N的倍數個slave,可是根據個人試驗數據,效果跟N個差很少,所以只須要啓動Nslave便可。 

no_web形式啓動locust:

若是採用no_web形式,則需使用--no-web參數,並會用到以下幾個參數。

  • -c, --clients:指定併發用戶數;
  • -r, --hatch-rate:指定併發加壓速率,默認值位1。

示例:

$ locust -f    locustfile.py     --host = xxxxx.com   --no-web -c 2 -r 1

在此基礎上,當咱們想要調試Locust腳本時,就能夠在腳本中須要調試的地方經過print打印日誌,而後將併發數和總執行次數都指定爲1

$ locust -f    locustfile.py     --host = xxxxx.com   --no-web -c 1 -r 1

執行測試

經過這種方式,咱們就能很方便地對Locust腳本進行調試了。

Locust腳本調試經過後,就算是完成了全部準備工做,能夠開始進行壓力測試了。

web形式啓動locust:

若是採用web形式,,則一般狀況下無需指定其它額外參數,Locust默認採用8089端口啓動web;若是要使用其它端口,就可使用以下參數進行指定。

  • -P, --port:指定web端口,默認爲8089.
  •  終端中--->進入到代碼目錄: locust     -f    locustfile.py     --host = xxxxx.com      
  • -f            指定性能測試腳本文件
  • -host      被測試應用的URL地址【若是不填寫,讀取繼承(HttpLocust)類中定義的host】
  • 若是Locust運行在本機,在瀏覽器中訪問http://localhost:8089便可進入Locust的Web管理頁面;若是Locust運行在其它機器上,那麼在瀏覽器中訪問http://locust_machine_ip:8089便可。

多進程分佈式運行

無論是單機多進程,仍是多機負載模式,運行方式都是同樣的,都是先運行一個master,再啓動多個slave

啓動master時,須要使用--master參數;一樣的,若是要使用8089之外的端口,還須要使用-P, --port參數。

  1. D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --master --port=8089
  2. [2018-06-05 15:36:30,654] dengshihuang/INFO/locust.main: Starting web monitor at *:8089
  3. [2018-06-05 15:36:30,684] dengshihuang/INFO/locust.main: Starting Locust 0.8.1

啓動slave時須要使用--slave參數;在slave中,就不須要再指定端口了。master啓動後,還須要啓動slave才能執行測試任務。

  1. D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --slave
  2. [2018-06-05 15:36:30,654] dengshihuang/INFO/locust.main: Starting web monitor at *:8089
  3. [2018-06-05 15:36:30,684] dengshihuang/INFO/locust.main: Starting Locust 0.8.1
  1. D:\workSpaces\ApiAutoTest\TestCases\OpsUltraAPITest\MonitorAPITest>locust -f monitorAgent.py --slave --master-host=<locust_machine_ip>

masterslave都啓動完畢後,就能夠在瀏覽器中經過http://locust_machine_ip:8089進入Locust的Web管理頁面了。使用方式跟單進程web形式徹底相同,只是此時是經過多進程負載來生成併發壓力,在web管理界面中也能看到實際的slave數量。若是slavemaster不在同一臺機器上,還須要經過--master-host參數再指定master的IP地址。

運行結果:

 Number of users to simulate    設置虛擬用戶數,對應中no_web模式的-c, --clients參數;

 Hatch rate(users spawned/second)每秒產生(啓動)的虛擬用戶數 , 對應着no_web模式的-r, --hatch-rate參數,默認爲1。點擊Start swarming 按鈕,開始運行性能測試。

上圖:啓動了一個 master 和兩個 slave,由兩個 slave 來向被測試系統發送請求

性能測試參數

  • Type: 請求的類型,例如GET/POST。

  • Name:請求的路徑。這裏爲百度首頁,即:https://www.baidu.com/

  • request:當前請求的數量。

  • fails:當前請求失敗的數量。

  • Median:中間值,單位毫秒,一半的服務器響應時間低於該值,而另外一半高於該值。

  • Average:平均值,單位毫秒,全部請求的平均響應時間。

  • Min:請求的最小服務器響應時間,單位毫秒。

  • Max:請求的最大服務器響應時間,單位毫秒。

  • Content Size:單個請求的大小,單位字節。

  • reqs/sec:是每秒鐘請求的個數。

 相比於LoadRunnerLocust的結果展現十分簡單,主要就四個指標:併發數RPS響應時間異常率。但對於大多數場景來講,這幾個指標已經足夠了。

在上圖中,RPS平均響應時間這兩個指標顯示的值都是根據最近2秒請求響應數據計算獲得的統計值,咱們也能夠理解爲瞬時值。

若是想看性能指標數據的走勢,就能夠在Charts欄查看。在這裏,能夠查看到RPS平均響應時間在整個運行過程當中的波動狀況。

除了以上數據,Locust還提供了整個運行過程數據的百分比統計值,例如咱們經常使用的90%響應時間響應時間中位值;平均響應時間和錯誤數的統計,該數據能夠經過Download response time distribution CSV和Download request statistics CSV得到,數據展現效果以下所示。

 

-----------------------------------------------------------

注意:

locust雖然使用方便,可是加壓性能和響應時間上面仍是有差距的,若是項目有很是大的併發加壓請求,能夠選擇wrk

對比方法與結果:

能夠準備兩臺服務器,服務器A做爲施壓方,服務器B做爲承壓方
服務器B上簡單的運行一個nginx服務就好了

服務器A上能夠安裝一些經常使用的壓測工具,好比locust、ab、wrk

實測下來,施壓能力上 wrk > golang >> ab > locust

由於locust一個進程只使用一核CPU,因此用locust壓測時,必須使用主從分佈式(zeromq通信)模式,並根據服務器CPU核數來起slave節點數

wrk約爲55K QPSgolang net/http 約 45K QPSab 大約 15K QPSlocust 最差,並且response time明顯比較長

相關文章
相關標籤/搜索