前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >性能测试工具--Locust官方文档(API)解读(全)

性能测试工具--Locust官方文档(API)解读(全)

作者头像
软测小生
发布2020-03-05 10:02:05
23.5K1
发布2020-03-05 10:02:05
举报
文章被收录于专栏:软测小生软测小生
文章略长 Locust学习笔记汇总

若有错误,请指正

内容以官网为准 https://locust.io/

Locust简介 Locust是什么?

Locust是一个简单易用的分布式用户负载测试工具。它用于web站点(或其他系统)的负载测试,并计算一个系统可以处理多少并发用户。

在测试中,一群locusts(蝗虫)会攻击你的网站。每个locusts (或者测试用户)的行为由您定义,集群过程由web UI实时监控。这将帮助你在允许实际用户使用之前测试并确定代码中的瓶颈。

Locust完全基于事件,因此可以在一台计算机上支持数千个并发用户。与许多其他基于事件的应用程序不同,它不使用回调。相反,它通过gevent使用轻量级进程。每个聚集站点上的蝗虫实际上是在它自己的进程中运行的(正确的说是greenlet)。这允许你使用Python编写非常有表现力的场景,而不用回调使代码复杂化。

Locust功能特性

  • 用普通的Python编写用户测试场景 不需要笨拙的UI或庞大的XML,只需像通常那样编码即可。基于协程而不是回调,您的代码看起来和行为都与正常的、阻塞Python代码一样。
  • 分布式和可扩展——支持成千上万的用户 Locust支持在多台机器上运行负载测试。由于基于事件,即使一个Locust节点也可以在一个进程中处理数千个用户。这背后的部分原因是,即使你模拟了那么多用户,也不是所有用户都积极的访问你的系统。通常,用户无所事事,想知道下一步该怎么做。每秒请求数不等于在线用户数
  • 基于web的UI Locust具有简洁的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一起安装。

代码语言:javascript
复制
$ pip3 install locust

如果您想要最新版本,可以使用pip直接从我们的Git存储库安装。例如,要使用Python 3安装master分支:

代码语言:javascript
复制
pip3 install -e git://github.com/locustio/locust.git@master#egg=locustio

安装了Locust之后,您的shell中应该可以使用Locust命令。(如果您没有使用virtualenv—您应该使用—请确保您的python脚本目录位于您的路径上)。

要查看可用的选项,请运行:

代码语言:javascript
复制
$ locust --help

Python版本支持

在Python 3.6、3.7和3.8

Windows上安装locust

在Windows上,运行pip install locustio 即可。

但是,如果没有成功,那么可以先安装为pyzmqgeventgreenlet预先构建的二进制包来修复它。

你可以在这里找到一个非官方的集合,预先建立的python包的windows: http://www.lfd.uci.edu/~gohlke/pythonlibs/

下载预构建的.whl文件后,可以使用以下方法进行安装:

代码语言:javascript
复制
$ pip install name-of-file.whl

然后可以使用pip install locustio进行安装。

注意: 在Windows上运行Locust应该可以很好地开发和测试您的负载测试脚本。但是,在运行大规模测试时,建议您在Linux机器上这样做,因为gevent在Windows下的性能很差。

macOS上安装Locust

当前是使用Homebrew在OS X上安装gevent的最短路径。

  • 安装Homebrew。
  • 安装libev(gevent的依赖):
代码语言:javascript
复制
brew install libev

增加打开文件最大数限制

机器上的每个HTTP连接打开一个新文件(技术上称为文件描述符)。操作系统可以为可打开的文件的最大数量设置一个较低的限制。如果该限制小于测试中模拟用户的数量,则会发生故障。

将操作系统的默认最大文件数限制增加到大于你要运行的模拟用户数的数量。如何执行此操作取决于所使用的操作系统。

快速入门Locust

例子locustfile.py 下面是一个简单的locustfile.py的小例子:

代码语言:javascript
复制
from locust import HttpLocust, TaskSet, between

def login(l):
    l.client.post("/login", {"username":"ellen_key", "password":"education"})

def logout(l):
    l.client.post("/logout", {"username":"ellen_key", "password":"education"})

def index(l):
    l.client.get("/")

def profile(l):
    l.client.get("/profile")

class UserBehavior(TaskSet):
    tasks = {index: 2, profile: 1}

    def on_start(self):
        login(self)

    def on_stop(self):
        logout(self)

class WebsiteUser(HttpLocust):
    task_set = UserBehavior
    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装饰器。下面代码与上面的代码效果一样:

代码语言:javascript
复制
from locust import HttpLocust, TaskSet, task, between

class UserBehaviour(TaskSet):
    def on_start(self):
        """ on_start is called when a Locust start before any task is scheduled """
        self.login()

    def on_stop(self):
        """ on_stop is called when the TaskSet is stopping """
        self.logout()

    def login(self):
        self.client.post("/login", {"username":"ellen_key", "password":"education"})

    def logout(self):
        self.client.post("/logout", {"username":"ellen_key", "password":"education"})

    @task(2)
    def index(self):
        self.client.get("/")

    @task(1)
    def profile(self):
        self.client.get("/profile")

class WebsiteUser(HttpLocust):
    task_set = UserBehaviour
    wait_time = between(5, 9)

Locust类(以及HttpLocust,因为它是一个子类)还允许您指定任务执行之间的等待时间(wait_time = between(5, 9))以及其他用户行为。使用between函数,可以在指定的最大值和最小值之间随机选择时间,但是可以通过将wait_time设置为任意函数来使用任何用户定义的时间分布。例如,对于平均时间为1秒的指数分布等待时间:

代码语言:javascript
复制
import random

class WebsiteUser(HttpLocust):
    task_set = UserBehaviour
    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,这将特别有用:

代码语言:javascript
复制
$ locust -f examples/basic.py --csv=example --no-web -t10m

文件将被命名为example_response_times.csvexample_stats.csv (使用--csv=example)并记录Locust构建的信息。

如果你想要更快(慢)的写入速度,也可以自动以写入频率:

代码语言:javascript
复制
import locust.stats
# 默认为2秒
locust.stats.CSV_STATS_INTERVAL_SEC = 5

此数据将写入两个文件,并将_response_times.csv_stats.csv添加到你提供的名称中:

代码语言:javascript
复制
$ cat example_response_times.csv
"Name","# requests","50%","66%","75%","80%","90%","95%","98%","99%","99.9%","99.99%","100%"
"GET /",31,4,4,4,4,4,4,4,4,4,4,4
"/does_not_exist",0,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A"
"GET /stats/requests",38,3,4,4,4,4,5,5,5,5,5,5
"None Total",69,3,4,4,4,4,4,5,5,5,5,5

代码语言:javascript
复制
$ cat example_stats.csv
"Type","Name","# requests","# failures","Median response time","Average response time","Min response time","Max response time","Average Content Size","Requests/s"
"GET","/",51,0,4,3,2,6,12274,0.89
"GET","/does_not_exist",0,56,0,0,0,0,0,0.00
"GET","/stats/requests",58,0,3,3,2,5,1214,1.01
"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 。它用于使模拟用户在每次执行任务后等待介于最小值和最大值之间的随机时间。其他内置的等待时间函数是constantconstant_pacing

使用以下locustfile,每个用户将在任务之间等待5到15秒:

代码语言:javascript
复制
from locust import Locust, TaskSet, task, between

class MyTaskSet(TaskSet):
    @task
    def my_task(self):
        print("executing my_task")

class User(Locust):
    task_set = MyTaskSet
    wait_time = between(5, 15)

wait_time方法应该返回秒数(或几分之一秒),也可以在TaskSet类上声明,在这种情况下,它将仅用于该TaskSet。

也可以直接在Locust或TaskSet类上声明自己的wait_time方法。接下来的Locust类将开始休眠1秒钟,然后休眠1秒,2秒,3秒,等等。

代码语言:javascript
复制
class MyLocust(Locust):
    task_set = MyTaskSet
    last_wait_time = 0

    def wait_time(self):
        self.last_wait_time += 1
        return self.last_wait_time

weight (权重)属性

如果文件中存在多个locust类,并且命令行中没有指定locust,则每个新生成的locust将从现有locust中随机选择。否则,你可以从相同的文件中像下面这样指定使用哪个locust: $ locust -f locust_file.py WebUserLocust MobileUserLocust

如果你想让这些locust中的一个执行得更频繁,你可以给这些类设置一个权重属性。例如,Web用户是mobile用户的三倍:

代码语言:javascript
复制
class WebUserLocust(Locust):
    weight = 3
    ...

class MobileUserLocust(Locust):
    weight = 1
    ...

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装饰器。

这里有一个例子:

代码语言:javascript
复制
from locust import Locust, TaskSet, task

class MyTaskSet(TaskSet):
    @task
    def my_task(self):
        print("Locust instance (%r) executing my_task" % (self.locust))

class MyLocust(Locust):
    task_set = MyTaskSet

@task使用可选的weight参数,该参数可用于指定任务的执行率。在以下示例中,task2的执行量是task1的两倍:

代码语言:javascript
复制
from locust import Locust, TaskSet, task
from locust.wait_time import between

class MyTaskSet(TaskSet):
    wait_time = between(5, 15)

    @task(3)
    def task1(self):
        pass

    @task(6)
    def task2(self):
        pass

class MyLocust(Locust):
    task_set = MyTaskSet

tasks属性

使用@task装饰器来声明任务是一种方便的方法,通常也是最好的方法。但是,也可以通过设置tasks属性来定义TaskSet的任务(使用@task装饰器实际上只填充tasks属性)。

tasks属性要么是python可调用项的列表,要么是 字典。这些任务是接收一个参数的python可调用函数——正在执行任务的TaskSet类实例。 这些任务是接收一个参数的python可调用函数——正在执行任务的TaskSet类实例。

代码语言:javascript
复制
from locust import Locust, TaskSet

def my_task(l):
    pass

class MyTaskSet(TaskSet):
    tasks = [my_task]

class MyLocust(Locust):
    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:

代码语言:javascript
复制
class ForumPage(TaskSet):
    @task(20)
    def read_thread(self):
        pass

    @task(1)
    def new_thread(self):
        pass

    @task(5)
    def stop(self):
        self.interrupt()

class UserBehaviour(TaskSet):
    tasks = {ForumPage:10}

    @task
    def index(self):
        pass

因此,在上面的示例中,如果在执行UserBehaviour TaskSet时选择了要执行的ForumPage,则ForumPage TaskSet将开始执行。然后,ForumPage TaskSet将选择其自己的任务之一,执行它,等待,依此类推。

关于上述示例,需要注意一点,那就是在ForumPage的stop方法中调用self.interrupt()。这样做实际上是停止执行ForumPage任务集,并在UserBehaviour实例中继续执行。如果我们在ForumPage的某处没有调用interrupt()方法,Locust将永远不会停止运行已经启动的ForumPage任务。但是通过使用中断功能,我们可以与任务权重一起定义模拟用户离开论坛的可能性。

也可以使用@task装饰器在类中内联声明嵌套的TaskSet,就像声明普通任务时一样:

代码语言:javascript
复制
class MyTaskSet(TaskSet):
    @task
    class SubTaskSet(TaskSet):
        @task
        def my_task(self):
            pass

引用Locust实例,或父TaskSet实例

TaskSet实例的属性locust指向它的locust实例,属性parent指向它的父TaskSet(它将在基本TaskSet中指向Locust实例)。

TaskSequence类

TaskSequence类是一个TaskSet,但是它的任务是按顺序执行的。 要定义此顺序,您应该执行以下操作:

代码语言:javascript
复制
    @seq_task(1)
    def first_task(self):
        pass

    @seq_task(2)
    def second_task(self):
        pass

    @seq_task(3)
    @task(10)
    def third_task(self):
        pass

在上面的示例中,执行顺序定义为先执行first_task,然后执行second_task,最后执行Third_task 10次。可以看到,可以使用@task装饰器组合@seq_task,当然也可以将TaskSet嵌套在TaskSequences中,反之亦然。

Setups, Teardowns, on_start, 和on_stop

Locust还支持Locust级setupteardown,TaskSet级setupteardownTaskSet'、 on_starton_stop.

Setups 和 Teardowns

setupteardown ,无论是在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.core.TaskSet.on_stop()方法。

事件顺序

由于许多设置和清除操作是相互依赖的,因此以下是它们的执行顺序:

  1. Locust setup (一次)
  2. TaskSet setup (一次)
  3. TaskSet on_start (每个locust一次)
  4. TaskSet tasks…
  5. TaskSet on_stop (每个locust一次)
  6. TaskSet teardown (一次)
  7. 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/:

代码语言:javascript
复制
from locust import HttpLocust, TaskSet, task, between

class MyTaskSet(TaskSet):
    @task(2)
    def index(self):
        self.client.get("/")

    @task(1)
    def about(self):
        self.client.get("/about/")

class MyLocust(HttpLocust):
    task_set = MyTaskSet
    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是TaskSetHttpLocust类的实例:

代码语言:javascript
复制
response = self.client.get("/about")
print("Response status code:", response.status_code)
print("Response content:", response.text)

这里有一个POST请求的例子:

代码语言:javascript
复制
response = self.client.post("/login",
 {"username":"testuser", 
 "password":"secret"}
 )

安全模式

HTTP客户端配置为以safe_mode运行。这样做的目的是,由于连接错误、超时或类似原因而失败的任何请求都不会引发异常,而是返回一个空的虚拟Response对象。该请求将在Locust的统计信息中标记为失败。返回的虚拟Response的content属性将设置为None,其status_code=0。

手动控制请求是成功还是失败

默认情况下,除非HTTP响应代码为OK(<400),否则请求将被标记为失败的请求。大多数情况下,此默认值是你想要的。但是,有时(例如,在测试URL端点时,你期望返回404,或者在测试一个设计糟糕的系统时,即使出现错误也可能返回200 OK)——需要手动控制Locust是否应该将请求标记为成功或失败。

通过使用catch_response参数和with语句,即使响应代码正确,也可以将请求标记为失败:

代码语言:javascript
复制
with self.client.get("/", catch_response=True) as response:
    if response.content != b"Success":
        response.failure("Got wrong response")

正如可以将具有OK响应代码的请求标记为失败一样,也可以将catch_response参数与with语句一起使用,以标记导致HTTP错误代码的请求在统计中仍被报告为成功:

代码语言:javascript
复制
with self.client.get("/does_not_exist/", catch_response=True) as response:
    if response.status_code == 404:
        response.success()

将带有动态参数的URL请求分组

网站的url包含一些动态参数的页面是很常见的。通常在Locust的统计信息中将这些URL分组在一起是很有意义的。这可以通过将名称参数传递给HttpSession的不同请求方法来完成。

例如:

代码语言:javascript
复制
# 这些请求的统计数据将归入以下类别: /blog/?id=[id]
for i in range(10):
    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文件可以使用以下方法导入公共库:

代码语言:javascript
复制
sys.path.append(os.getcwd())
import 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

代码语言:javascript
复制
$ 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来执行此操作:

代码语言:javascript
复制
$ 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_MODE standalone、master 或者 slave。 默认是standalone。
  • LOCUSTFILE_PATH 容器内部到locustfile的路径。默认为/locustfile.py.
  • LOCUST_MASTER_HOST master的主机名。
  • 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:

代码语言:javascript
复制
$ locust -f locust_files/my_locust_file.py --no-web -c 1000 -r 100

-c 指定要生成的Locust用户数; -r 指定孵化率(每秒产生的用户数)。

为测试设置时间限制

如果要指定测试的运行时间,可以使用 --run-time 或者 -t:

代码语言:javascript
复制
$ locust -f --no-web -c 1000 -r 100 --run-time 1h30m

一旦时间到,Locust将关闭。

允许任务在关闭时完成其迭代

默认情况下,Locust将立即停止任务。如果想让任务完成迭代,则可以使用--stop-timeout <seconds>

代码语言:javascript
复制
$ 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:

代码语言:javascript
复制
from locust import TaskSet, task, between
from locust.contrib.fasthttp import FastHttpLocust

class MyTaskSet(TaskSet):
    @task
    def index(self):
        response = self.client.get("/")

class MyLocust(FastHttpLocust):
    task_set = MyTaskSet
    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)

content Unzips, 如有必要,并缓冲接收到的Body. 小心大文件!

headers= None 类似于Dict的对象,包含响应标头

text 以解码字符串的形式返回响应的文本内容(python2上的unicode)

Locust--CSV存储测试数据

Locust的测试结果保存到CSV文件,在这种情况下,有两种方法可以执行此操作。

首先,通过Web UI运行Locust时,可以在“Download Data”选项卡下得到CSV文件。

其次,可以使用标签运行Locust,该标签将定期保存两个CSV文件。如果计划使用--no-web标签以自动化方式运行Locust,这将特别有用:

代码语言:javascript
复制
$ locust -f examples/basic.py --csv=example --no-web -t10m

文件将被命名为example_response_times.csvexample_stats.csv (使用--csv=example)并记录Locust构建的信息。

如果你想要更快(慢)的写入速度,也可以自动以写入频率:

代码语言:javascript
复制
import locust.stats
# 默认为2秒
locust.stats.CSV_STATS_INTERVAL_SEC = 5

此数据将写入两个文件,并将_response_times.csv_stats.csv添加到你提供的名称中:

代码语言:javascript
复制
$ cat example_response_times.csv
"Name","# requests","50%","66%","75%","80%","90%","95%","98%","99%","99.9%","99.99%","100%"
"GET /",31,4,4,4,4,4,4,4,4,4,4,4
"/does_not_exist",0,"N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A","N/A"
"GET /stats/requests",38,3,4,4,4,4,5,5,5,5,5,5
"None Total",69,3,4,4,4,4,4,5,5,5,5,5

代码语言:javascript
复制
$ cat example_stats.csv
"Type","Name","# requests","# failures","Median response time","Average response time","Min response time","Max response time","Average Content Size","Requests/s"
"GET","/",51,0,4,3,2,6,12274,0.89
"GET","/does_not_exist",0,56,0,0,0,0,0,0.00
"GET","/stats/requests",58,0,3,3,2,5,1214,1.01
"None","Total",109,56,3,3,2,6,6389,1.89

使用定制的客户端测试其他系统

以HTTP为主要目标构建Locust。但是,通过编写触发request_successrequest_failure事件的自定义客户端,可以很容易的将其扩展,用来对任何基于request/response的系统进行负载测试。

Locust client示例--XML-RPC

以下是Locust类XmlRpcLocust的示例,该类提供XML-RPC客户端XmlRpcClient并跟踪所有发出的请求:

代码语言:javascript
复制
import time
import xmlrpclib

from locust import Locust, TaskSet, events, task, between


class XmlRpcClient(xmlrpclib.ServerProxy):
    """
 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    """
    def __getattr__(self, name):
        func = xmlrpclib.ServerProxy.__getattr__(self, name)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            try:
                result = func(*args, **kwargs)
            except xmlrpclib.Fault as e:
                total_time = int((time.time() - start_time) * 1000)
                events.request_failure.fire(request_type="xmlrpc", name=name, response_time=total_time, exception=e)
            else:
                total_time = int((time.time() - start_time) * 1000)
                events.request_success.fire(request_type="xmlrpc", name=name, response_time=total_time, response_length=0)
                # In this example, I've hardcoded response_length=0. If we would want the response length to be 
                # reported correctly in the statistics, we would probably need to hook in at a lower level

        return wrapper


class XmlRpcLocust(Locust):
    """
33    This is the abstract Locust class which should be subclassed. It provides an XML-RPC client
34    that can be used to make XML-RPC requests that will be tracked in Locust's statistics.
35    """
    def __init__(self, *args, **kwargs):
        super(XmlRpcLocust, self).__init__(*args, **kwargs)
        self.client = XmlRpcClient(self.host)


class ApiUser(XmlRpcLocust):

    host = "http://127.0.0.1:8877/"
    wait_time = between(0.1, 1)

    class task_set(TaskSet):
        @task(10)
        def get_time(self):
            self.client.get_time()

        @task(5)
        def get_random_number(self):
            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_successlocust.events.request_failure的重要功能。

下面是 XML-RPC 服务器的实现,它可以作为上述代码的服务器:

代码语言:javascript
复制
import random
import time
from SimpleXMLRPCServer import SimpleXMLRPCServer


def get_time():
    time.sleep(random.random())
    return time.time()

def get_random_number(low, high):
    time.sleep(random.random())
    return random.randint(low, high)

server = SimpleXMLRPCServer(("localhost", 8877))
print("Listening on port 8877...")
server.register_function(get_time, "get_time")
server.register_function(get_random_number, "get_random_number")

Locust--Locust API以及延伸扩展--0.14.4

Locust延伸扩展

Locust附带了很多事件,这些事件提供了以不同方式扩展Locust的钩子(hooks )。

事件监听器可以在模块级注册到Locust文件中。这里有一个例子:

代码语言:javascript
复制
from locust import events

def my_success_handler(request_type, name, response_time, response_length, **kw):
    print "Successfully fetched: %s" % (name)

events.request_success += my_success_handler

注意: 强烈建议你在侦听器中添加通配符关键字参数(上面代码中的** kw),以防止在以后的版本中添加新参数时代码崩溃。 要查看所有可用事件,请参阅事件[挂钩](https://docs.locust.io/en/stable/api.html#events)。

添加网络路由

Locust使用Flask来提供Web UI,因此很容易将Web端点添加到Web UI。只需将Flask应用程序导入您的locustfile并设置一条新路径:

代码语言:javascript
复制
from locust import web

@web.app.route("/added_page")
def my_added_page():
    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= None TaskSet类,定义此Locust的执行行为

wait_time= None 该方法,返回执行Locust任务之间的时间(以秒为单位)。可以为单个TaskSet覆盖。 例如:

代码语言:javascript
复制
from locust import Locust, between
class User(Locust):
    wait_time = between(3, 25)

weight= 10 Locust被选中执行的概率。权重越高,被选中的机会越大。

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倍:

代码语言:javascript
复制
class ForumPage(TaskSet):
    tasks = {ThreadPage:15, write_post:1}

wait_time() 该方法返回执行任务之间的时间(以秒为单位)。 例如:

代码语言:javascript
复制
from locust import TaskSet, between
class Tasks(TaskSet):
    wait_time = between(3, 25)
任务装饰器 task decorator

task(weight=1) 使用一个便捷装的饰器,以便能够为类中的TaskSet内联声明任务。

代码语言:javascript
复制
class ForumPage(TaskSet):
    @task(100)
    def read_thread(self):
        pass

    @task(7)
    def create_thread(self):
        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() 该方法返回执行任务之间的时间(以秒为单位)。 例如:

代码语言:javascript
复制
from locust import TaskSet, between
class Tasks(TaskSet):
    wait_time = between(3, 25)
seq_task装饰器

seq_task(order) 用于在类中内联声明 TaskSequence 的任务。 例如:

代码语言:javascript
复制
class NormalUser(TaskSequence):
    @seq_task(1)
    def login_first(self):
        pass

    @seq_task(2)
    @task(25) # You can also set the weight in order to execute the task for `weight` times one after another.
    def then_read_thread(self):
        pass

    @seq_task(3)
    def then_logout(self):
        pass
HttpSession 类

between(min_wait, max_wait) 返回一个函数,该函数将在min_wait和max_wait之间返回一个随机数。 例如:

代码语言:javascript
复制
class User(Locust):
    # wait between 3.0 and 10.5 seconds after each task
    wait_time = between(3.0, 10.5)

constant(wait_time) 返回一个函数,该函数只返回wait_time参数指定的数字。 例如:

代码语言:javascript
复制
class User(Locust):
    wait_time = constant(3)

constant_pacing(wait_time) 返回一个函数,该函数将跟踪任务的运行时间,每次调用它时,它将返回一个等待时间,该等待时间将尝试使任务执行之间的总时间等于wait_time参数指定的时间。

在以下示例中,无论任务执行时间如何,任务总是每秒执行一次:

代码语言:javascript
复制
class User(Locust):
    wait_time = constant_pacing(1)
    class task_set(TaskSet):
        @task
        def my_task(self):
            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:可选参数。是否立即下载响应内容。默认值为 Falseverify:可选参数。如果为True,则会验证 SSL 证书。也可以提供一个 CA_BUNDLE 路径。 cert:可选参数。如果提供一个字符串。那么应该是指向SSL 客户端证书(.pem文件)的路径;如果是一个元组,则应该是 (‘cert’, ‘key’)。

Response类

这个类其实是位于python-requests库中的,但是由于 Locust在构造HTTP 请求的时候要用到这个类,并且在编写 Locust负载测试时,这个类也非常重要,所以就把这个类包含在了 API 文档里。您还可以查看请求文档中的Response类。

class Response Response对象,它包含服务器对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 类的子类。包含两个额外的方法:successfailure

failure(exc) 将响应报告为失败。 其中参数 exc 可以是一个Python的异常类或者一个字符串。如果是一个字符串,那么将使用这个字符串来实例化 CatchResponseError 类。 例如:

代码语言:javascript
复制
with self.client.get("/", catch_response=True) as response:
    if response.content == b"":
        response.failure("No data")

success() 报告响应成功 例如:

代码语言:javascript
复制
with self.client.get("/does/not/exist", catch_response=True) as response:
    if response.status_code == 404:
        response.success()

InterruptTaskSet异常 exception InterruptTaskSet(reschedule=True) 在 Locust 任务内抛出这个异常时,将会中断这个 Locust 正在执行的当前任务。

事件钩子

事件钩子都是 locust.events.EventHook 类的实例。

class EventHook 简单事件类,用于为 locust 中不同类型的事件提供钩子。

下面的代码演示如何使用这个类:

代码语言:javascript
复制
my_event = EventHook()
def on_my_event(a, b, **kw):
    print "Event was fired with arguments: %s, %s" % (a, b)
my_event += on_my_event
my_event.fire(a="foo", b="bar")

如果 reverse 的值为 True,则处理程序将按照插入时的相反顺序运行。

注意: 强烈建议你在事件监听器中添加通配符关键字参数,以防止在以后的版本中添加新参数时代码中断。

可用的钩子

下面的事件钩子在 locust.events 模块下可用: request_success= <locust.events.EventHook object> 当一个请求成功完成时触发。 监听者应该使用如下参数:

  • request_type:使用的请求方法。
  • name:被调用的URL的路径(如果在对客户端的调用中使用了名称,则重写名称)。
  • response_time:使用毫秒表示的响应时间。
  • response_length:响应的 Content-Length 值。

request_failure= <locust.events.EventHook object> 当一个请求失败时触发。 事件触发式将使用如下参数:

  • request_type:使用的请求方法。
  • name:被调用的URL的路径(如果在对客户端的调用中使用了名称,则重写名称)。
  • response_time:用毫秒表示的从发出请求到抛出异常时的时间间隔。
  • exception:抛出的异常的实例。

locust_error= <locust.events.EventHook object> 当 Locust 类的执行过程中出现异常时触发。 事件触发式将使用如下参数:

  • locust_instance:异常发生时的 Locust 类的实例。
  • exception:抛出的异常。
  • tb:回溯对象(从 sys.exc_info()[2] 得到)

report_to_master= <locust.events.EventHook object> 当 Locust 在 -slave 模式下运行时使用。用于将数据附加到定期发送给主服务器的数据字典上。当报告要发送到主服务器时,它会定期触发。

注意: Locust 使用的键 ‘stats’ 和 ‘errors’ 不应该被覆盖。

事件触发式将使用如下参数:

  • client_id:正在运行的 Locust 进程的客户端 ID。
  • data:可修改的数据字典,以便附加应发送到主服务器的数据。

slave_report= <locust.events.EventHook object> 当 locust 在 -master 模式下运行使用。并在 Locust 主服务器从从属服务器收到报告时触发。 此事件可用于聚合来自 Locust 从属服务器的数据。 事件触发式将使用如下参数:

  • client_id:报告 Locust 从属服务器的客户端 ID。
  • data:来自从属节点的数据字典。

hatch_complete= <locust.events.EventHook object> 当所有 locust 用户都已经生成时触发。 事件触发式将使用如下参数:

  • user_count:孵化出的用户数量。

quitting= <locust.events.EventHook object> 在退出 locust 进程时触发。

第三方工具

支持其他采样器协议,报告等。 Locust 插件:https://github.com/SvenskaSpel/locust-plugins/

无需手动步骤即可自动执行分布式运行 Locust集群:https://github.com/SvenskaSpel/locust-swarm/(蝗虫群)

使用其他语言 Locust主服务器和Locust从服务器通过交换msgpack消息进行通信,这是许多语言所支持的。因此,您可以使用任何喜欢的语言编写Locust任务。为了方便起见,一些库充当了从属运行程序。他们运行你的Locust任务,并定期向master报告。

Golang Boomer:https://github.com/myzhan/boomer/

Java Locust4j:https://github.com/myzhan/locust4j Swarm: https://github.com/anhldbk/swarm

配置管理 部署Locust很容易,但是有些工具仍然可以提供一定程度的便利。

tinx.locust是Ansible的一个安装角色,用于配置和控制Locust系统服务,或使用ansible-container构建Locust docker映像。还管理locustfile和相应的测试数据。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 软测小生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Locust简介 Locust是什么?
  • Locust功能特性
  • Locust背景
  • 安装
  • Python版本支持
  • Windows上安装locust
  • macOS上安装Locust
  • 增加打开文件最大数限制
  • 快速入门Locust
  • 启动Locust
  • 打开Locust的Web界面
    • Locust--CSV存储测试数据
      • 编写一个locustfile
      • Locust类
      • task_set属性
      • wait_time属性
      • weight (权重)属性
      • host属性
      • TaskSet类
      • 声明任务
      • tasks属性
      • TaskSets可嵌套
      • 引用Locust实例,或父TaskSet实例
        • TaskSequence类
        • Setups, Teardowns, on_start, 和on_stop
        • Setups 和 Teardowns
        • on_start和on_stop方法
        • 事件顺序
        • 发送HTTP请求
        • 使用HTTP客户端
        • 安全模式
        • 手动控制请求是成功还是失败
        • 将带有动态参数的URL请求分组
        • 公共库--Common libraries
          • Locust--分布式运行
          • 使用Docker进行分布式运行
          • 非UI模式下分布式运行Locust
          • 逐步负载模式下分布式运行Locust
          • 提高蝗虫的性能
            • Locust--逐步负载模式
            • 选项
            • Locust在非Web UI的情况下“逐步负载”模式运行
            • Locust在逐步负载模式下分布式运行
              • Locust--Docker运行Locust
              • 环境变量
              • 运行测试
                • Locust--非UI模式下运行Locust
                • 为测试设置时间限制
                • 允许任务在关闭时完成其迭代
                • 非Web UI的情况下分布式运行Locust
                  • Locust--使用更快的HTTP client提高Locust性能
                  • 如何使用FastHttpLocust
                  • API
                  • FastHttpSession类
                    • Locust--CSV存储测试数据
                      • 使用定制的客户端测试其他系统
                      • Locust client示例--XML-RPC
                        • Locust--Locust API以及延伸扩展--0.14.4
                          • Locust延伸扩展
                          • 添加网络路由
                          • Locust日志记录
                          • Options选项
                        • Locust API
                          • Locust 类
                          • HTTPLocust 类
                          • TaskSet类
                          • 任务装饰器 task decorator
                          • TaskSequence类(任务序列类)
                          • seq_task装饰器
                          • HttpSession 类
                          • HttpSession 类
                          • Response类
                          • ResponseContextManager类
                      • 第三方工具
                      相关产品与服务
                      容器服务
                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档