python:locust库进行负载测试

安装

C:\Users\lifeng01>pip install locust
Collecting locust
  Downloading locust-2.5.0-py3-none-any.whl (795 kB)
     |████████████████████████████████| 795 kB 364 kB/s
Requirement already satisfied: msgpack>=0.6.2 in d:\python\python37\lib\site-packages (from locust) (1.0.2)
Requirement already satisfied: ConfigArgParse>=1.0 in d:\python\python37\lib\site-packages (from locust) (1.4)
Collecting geventhttpclient>=1.5.1
  Downloading geventhttpclient-1.5.3-cp37-cp37m-win_amd64.whl (36 kB)
Requirement already satisfied: requests>=2.9.1 in d:\python\python37\lib\site-packages (from locust) (2.25.0)
Collecting pyzmq>=22.2.1
  Downloading pyzmq-22.3.0-cp37-cp37m-win_amd64.whl (1.1 MB)
     |████████████████████████████████| 1.1 MB 252 kB/s
Collecting Flask-Cors>=3.0.10
  Downloading Flask_Cors-3.0.10-py2.py3-none-any.whl (14 kB)
Requirement already satisfied: psutil>=5.6.7 in d:\python\python37\lib\site-packages (from locust) (5.8.0)
Requirement already satisfied: typing-extensions>=3.7.4.3 in d:\python\python37\lib\site-packages (from locust) (3.7.4.3)
Collecting Werkzeug>=2.0.0
  Downloading Werkzeug-2.0.2-py3-none-any.whl (288 kB)
     |████████████████████████████████| 288 kB 192 kB/s
Requirement already satisfied: Flask-BasicAuth>=0.2.0 in d:\python\python37\lib\site-packages (from locust) (0.2.0)
Requirement already satisfied: pywin32 in d:\python\python37\lib\site-packages (from locust) (228)
Collecting flask>=2.0.0
  Downloading Flask-2.0.2-py3-none-any.whl (95 kB)
     |████████████████████████████████| 95 kB 270 kB/s
Collecting roundrobin>=0.0.2
  Downloading roundrobin-0.0.2.tar.gz (2.4 kB)
  Preparing metadata (setup.py) ... done
Requirement already satisfied: gevent>=20.9.0 in d:\python\python37\lib\site-packages (from locust) (21.1.2)
Collecting Jinja2>=3.0
  Downloading Jinja2-3.0.3-py3-none-any.whl (133 kB)
     |████████████████████████████████| 133 kB 252 kB/s
Requirement already satisfied: click>=7.1.2 in d:\python\python37\lib\site-packages (from flask>=2.0.0->locust) (7.1.2)
Collecting itsdangerous>=2.0
  Using cached itsdangerous-2.0.1-py3-none-any.whl (18 kB)
Requirement already satisfied: Six in d:\python\python37\lib\site-packages (from Flask-Cors>=3.0.10->locust) (1.12.0)
Requirement already satisfied: zope.interface in d:\python\python37\lib\site-packages (from gevent>=20.9.0->locust) (5.3.0)
Requirement already satisfied: zope.event in d:\python\python37\lib\site-packages (from gevent>=20.9.0->locust) (4.5.0)
Requirement already satisfied: setuptools in d:\python\python37\lib\site-packages (from gevent>=20.9.0->locust) (41.2.0)
Requirement already satisfied: cffi>=1.12.2 in d:\python\python37\lib\site-packages (from gevent>=20.9.0->locust) (1.14.5)
Requirement already satisfied: greenlet<2.0,>=0.4.17 in d:\python\python37\lib\site-packages (from gevent>=20.9.0->locust) (1.0.0)
Requirement already satisfied: certifi in d:\python\python37\lib\site-packages (from geventhttpclient>=1.5.1->locust) (2020.4.5.1)
Requirement already satisfied: brotli in d:\python\python37\lib\site-packages (from geventhttpclient>=1.5.1->locust) (1.0.9)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in d:\python\python37\lib\site-packages (from requests>=2.9.1->locust) (1.26.2)
Requirement already satisfied: idna<3,>=2.5 in d:\python\python37\lib\site-packages (from requests>=2.9.1->locust) (2.9)
Requirement already satisfied: chardet<4,>=3.0.2 in d:\python\python37\lib\site-packages (from requests>=2.9.1->locust) (3.0.4)
Requirement already satisfied: pycparser in d:\python\python37\lib\site-packages (from cffi>=1.12.2->gevent>=20.9.0->locust) (2.20)
Collecting MarkupSafe>=2.0
  Using cached MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl (14 kB)
Using legacy 'setup.py install' for roundrobin, since package 'wheel' is not installed.
Installing collected packages: MarkupSafe, Werkzeug, Jinja2, itsdangerous, flask, roundrobin, pyzmq, geventhttpclient, Flask-Cors, locust
  Attempting uninstall: MarkupSafe
    Found existing installation: MarkupSafe 1.1.1
    Uninstalling MarkupSafe-1.1.1:
      Successfully uninstalled MarkupSafe-1.1.1
  Attempting uninstall: Werkzeug
    Found existing installation: Werkzeug 1.0.1
    Uninstalling Werkzeug-1.0.1:
      Successfully uninstalled Werkzeug-1.0.1
  Attempting uninstall: Jinja2
    Found existing installation: Jinja2 2.11.2
    Uninstalling Jinja2-2.11.2:
      Successfully uninstalled Jinja2-2.11.2
  Attempting uninstall: itsdangerous
    Found existing installation: itsdangerous 1.1.0
    Uninstalling itsdangerous-1.1.0:
      Successfully uninstalled itsdangerous-1.1.0
  Attempting uninstall: flask
    Found existing installation: Flask 1.1.2
    Uninstalling Flask-1.1.2:
      Successfully uninstalled Flask-1.1.2
    Running setup.py install for roundrobin ... done
  Attempting uninstall: pyzmq
    Found existing installation: pyzmq 22.0.3
    Uninstalling pyzmq-22.0.3:
      Successfully uninstalled pyzmq-22.0.3
  Attempting uninstall: geventhttpclient
    Found existing installation: geventhttpclient 1.4.4
    Uninstalling geventhttpclient-1.4.4:
      Successfully uninstalled geventhttpclient-1.4.4
Successfully installed Flask-Cors-3.0.10 Jinja2-3.0.3 MarkupSafe-2.0.1 Werkzeug-2.0.2 flask-2.0.2 geventhttpclient-1.5.3 itsdangerous-2.0.1 locust-2.5.0 pyzmq-22.3.0 roundrobin-0.0.2

准备入门

蝗虫(locust)测试本质上是一个Python程序。这使得它非常灵活,尤其擅长实现复杂的用户流。但它也可以做简单的测试,所以让我们从这开始:

# -*-coding:utf-8 -*-
# ** createDate: 2021/11/19 10:31
# ** scriptFile: locustfiles.py
# ** __author__: Li Feng
"""
注释信息:
"""


from locust import HttpUser, task


class HelloWorldUser(HttpUser):
    @task
    def hello_world(self):
        self.client.get("/hello")
        self.client.get("/world")

这个用户将一次又一次地向/hello/world发出HTTP请求。将代码放入当前目录下名为locustfile.py的文件中,并运行locust:

PS F:\project_gitee\Test\locustProject> locust -f locustfiles.py -H https://www.baidu.com
[2021-11-19 10:42:51,524] jszx-zlb/INFO/locust.main: Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces)
[2021-11-19 10:42:51,538] jszx-zlb/INFO/locust.main: Starting Locust 2.5.0

一旦您启动了蝗虫(locust),打开一个浏览器并指向http://localhost:8089。界面显示如下:

python:locust库进行负载测试_第1张图片

python:locust库进行负载测试_第2张图片

名词解释:

  • Number of users to simulate:设置模拟的用户总数
  • Spawn rate (users spawned/second):每秒启动的虚拟用户数
  • Host:待测域名
  • Start swarming:开始执行locust脚本按钮

注意点,如果您命令行没有输入-H加域名的话,web页面的的Host栏就是空的,就得需要你手动输入了。


以上是web页面操作,你也可以直接使用命令行的无头操作:

PS F:\project_gitee\Test\locustProject> locust --headless --users 10 --spawn-rate 0.5 -f locustfiles.py -H https://www.baidu.com
[2021-11-19 11:28:46,379] jszx-zlb-047/INFO/locust.main: No run time limit set, use CTRL+C to interrupt
[2021-11-19 11:28:46,380] jszx-zlb-047/INFO/locust.main: Starting Locust 2.5.0
 Name                                                                              # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
----------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                                             0     0(0.00%)  |       0       0       0       0  |    0.00    0.00

[2021-11-19 11:28:46,383] jszx-zlb-047/INFO/locust.runners: Ramping to 10 users at a rate of 0.50 per second
(......)
忽略了一些显示报告
(......)
[2021-11-19 11:29:19,597] jszx-zlb/INFO/locust.main: Running teardowns...
[2021-11-19 11:29:19,597] jszx-zlb/INFO/locust.main: Shutting down (exit code 1), bye.
[2021-11-19 11:29:19,597] jszx-zlb/INFO/locust.main: Cleaning up runner...
 Name                                                                              # reqs      # fails  |     Avg     Min     Max  Median  |   req/s failures/s
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 GET /hello                                                                          6662 6662(100.00%)  |      17       9     118      16  |  200.57  200.57
 GET /world                                                                          6657 6657(100.00%)  |      17       9     236      16  |  200.42  200.42
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregated                                                                         13319 13319(100.00%)  |      17       9     236      16  |  401.00  401.00

Response time percentiles (approximated)
 Type     Name                                                                                  50%    66%    75%    80%    90%    95%    98%    99%  99.9% 99.99%   100% # reqs
--------|--------------------------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
 GET      /hello                                                                                 16     17     18     18     24     30     41     75    100    120    120   6662
 GET      /world                                                                                 16     17     18     19     24     30     42     77    100    240    240   6657
--------|--------------------------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------|
 None     Aggregated                                                                             16     17     18     19     24     30     41     76    100    120    240  13319

Error report
 # occurrences      Error
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 93                 GET /hello: RemoteDisconnected('Remote end closed connection without response')
 6572               GET /world: HTTPError('404 Client Error: Not Found for url: https://www.baidu.com/world')
 6569               GET /hello: HTTPError('404 Client Error: Not Found for url: https://www.baidu.com/hello')
 84                 GET /world: RemoteDisconnected('Remote end closed connection without response')
 1                  GET /world: ConnectionResetError(10054, '远程主机强迫关闭了一个现有的连接。', None, 10054, None)
----------------------------------------------------------------------------------------------------------------------------------------------------------------

locustfiles.py文件的编写

首先我们看下官方提供的示例:

import time
from locust import HttpUser, task, between


class QuickstartUser(HttpUser):
    wait_time = between(1, 5)

    @task
    def hello_world(self):
        self.client.get("/hello")
        self.client.get("/world")

    @task(3)
    def view_items(self):
        for item_id in range(10):
            self.client.get(f"/item?id={item_id}", name="/item")
            time.sleep(1)

    def on_start(self):
        self.client.post("/login", json={"username":"foo", "password":"bar"})

引入的包:

  • time:时间库
  • HttpUser:是表示将生成一个HTTP用户,这个类会在实例化后保持请求之间的用户会话
  • task:修饰符,声明任务(如果没有这个就不会对这个接口运行)
  • between:返回一个函数,该函数返回一个介于min_wait和max_wait之间的随机数;还有一个是constant,它是设置固定的时间内

名词解释:

  • wait_time = between(1, 5):设置时间,最小为1,最大为5秒
  • @task(3):方法上装饰这个是声明这个是任务,3代表的是权重,默认权重是1
  • name="/item":是把该方法请求的10次,归类在item中
  • self.client.get:是去请求接口,发送数据
  • on_start:该方法是前置方法,优先运行的,通常放登录,登录会自动关联后续接口;还有一个后置是on_stop方法

官方提供的例子,复制黏贴到.py文件中后,直接命令行运行即可,虽然是报错的,但是不影响观看,哈哈:

PS F:\project_gitee\Test\locustProject> locust -f locustfiles.py
  • -f是指定文件

实践示例

以我们自己的项目为示例(代码中会有部分改动,主要是牵扯到一些隐私信息)进行负载测试:

import urllib3
from locust import HttpUser, TaskSet, task, between


class WebsiteLmsUser(TaskSet):
    wait_time = between(1, 5)

    def on_start(self):
        urllib3.disable_warnings()
        self.client.verify = False
        self.client.headers.update({"Content-Type": "application/json"})

        res = self.client.post("/api/auth", json={
            "username": "account01",
            "password": "123456",
            "scenario": "web",
            "day": 0
        })

        res_json = res.json()["data"]["token"]

        self.client.headers.update({"token": res_json})

    @task
    def user_info(self):
        self.client.get("/api/auth")

    @task
    def user_menu(self):
        self.client.get("/api/auth/menu")


class WebsiteUser(HttpUser):
    tasks = [WebsiteLmsUser]
    host = "https://api-first.com"
    min_wait = 1000
    max_wait = 5000

名词解释:

  • on_start中是写入了登录操作
  • WebsiteLmsUser继承是TaskSet类,该类是定义用户将执行的一组任务。
  • WebsiteUser继承了HttpUser类,该类是将生成一个HTTP用户,并请求将要进行负载测试的系统
  • tasks = [WebsiteLmsUser]这里是locust用户将运行python可调用类或者TaskSet类的集合
  • host = "https://api-first.com"指定了host,后面web页面就不需要输入了
  • min_wait和max_wait是定义最长和最短等待时间

命令行输入命令开始运行:

PS F:\project_gitee\Test\locustProject> locust -f locustfiles.py
[2021-11-19 18:41:14,428] jszx-zlb/INFO/locust.main: Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces)
[2021-11-19 18:41:14,442] jszx-zlb/INFO/locust.main: Starting Locust 2.5.0
[2021-11-19 18:41:25,250] jszx-zlb/INFO/locust.runners: Ramping to 1 users at a rate of 1.00 per second
[2021-11-19 18:41:25,251] jszx-zlb/INFO/locust.runners: All users spawned: {"WebsiteUser": 1} (1 total users)

运行情况如下:

python:locust库进行负载测试_第3张图片

python:locust库进行负载测试_第4张图片

python:locust库进行负载测试_第5张图片

生成报告如下:

python:locust库进行负载测试_第6张图片

python:locust库进行负载测试_第7张图片

python:locust库进行负载测试_第8张图片

实际运行过程中,locust一直执行的是负载测试,就是一直在持续运行,必须你自己手动停止,这样也能更好的检测系统接口性能的稳定性。此次只是基础的使用,这玩意还是很强大的,后续还有优化及分布等等。


以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,直接私信留言会及时修正发布;非常期待你的点赞和分享哟,谢谢!

未完,待续…

一直都在努力,希望您也是!

微信搜索公众号:就用python

作者:李 锋|编辑排版:梁莉莉

python:locust库进行负载测试_第9张图片 更多内容欢迎关注公众号

你可能感兴趣的