欢迎光临散文网 会员登陆 & 注册

Locust核心知识详解

2023-03-08 13:35 作者:慧测  | 我要投稿

一、什么是Locust

Locust 是一种易于使用、可直接使用pyhton编写脚本运行且可扩展的性能测试工具。

二、特点

  • 用纯 Python 编写测试场景

    如果您想要让用户执行循环、执行一些条件行为或进行一些计算,只需使用 Python 提供的常规编程结构即可。Locust 在其自己的 greenlet(轻量级进程/协程)中运行每个用户。这使您可以像编写常规(阻塞式)Python 代码一样编写测试,而无需使用回调或其他机制。因为您的场景“只是 Python”,您可以使用常规 IDE,并将测试版本控制为常规代码(而不是某些使用 XML 或二进制格式的其他工具)。

  • 分布式和可扩展——支持数十万并发用户

    Locust 使分布在多台机器上运行负载测试变得容易。它是基于事件的(使用 gevent),这使得单个进程可以处理许多千个并发用户。虽然可能存在其他工具能够在给定硬件上执行更多请求,但每个 Locust 用户的低开销使其非常适合测试高并发工作负载。

  • 基于 友好的Web 用户界面

    Locust 有一个用户友好的 Web 界面,实时显示测试的进度。您甚至可以在测试运行时更改负载。它也可以在没有用户界面的情况下运行,使其易于用于 CI/CD 测试。

  • 可以测试任何系统

    尽管 Locust 主要与网站/服务一起使用,但它可以用于测试几乎任何系统或协议。只需编写要测试的客户端,或者探索由社区创建的客户端。

  • 可入侵式的

    Locust 很小而且非常灵活,如果您想将报告数据发送到您喜欢的数据库和绘图系统,或者包装调用 REST API 来处理您系统的特定情况或运行完全自定义的负载模式,都是极其容易实现的!

三、名称和背景

Locust 起源于对现有解决方案的不满。没有现有的负载测试工具能够很好地生成对动态网站产生现实负载的能力,其中大多数页面对不同的用户具有不同的内容。现有的工具使用笨拙的界面或冗长的配置文件声明测试。在 Locust 中,我们采用了不同的方法。我们提供了一个 Python 框架,让您可以使用 Python 代码定义用户的行为,而不是配置格式或用户界面。

Locust 的名称来自于蝗虫物种,以其群集行为而闻名。

Locust 的早期版本使用了从自然界借鉴的术语(如集群、孵化、攻击等),但现在采用了更多行业标准的命名。

四、安装

五、编写测试

现在,让我们来看一个更完整、更实际的例子,展示你的测试可能会是什么样子:

让我们逐一分解它:

这是一个常规的 Python 模块,可以从其他文件或包中导入代码。

在这里,我们定义了一个类,用于模拟我们将要测试的用户。它继承了 HttpUser 类,为每个用户提供了一个 client 属性,该属性是 HttpSession 的一个实例,可以用于向我们想要进行负载测试的目标系统发出 HTTP 请求。当测试开始时,locust 将为它模拟的每个用户创建一个该类的实例,并且每个用户将在自己的 green gevent 线程中开始运行。

要成为一个有效的 locustfile,文件必须至少包含一个继承自 User 的类。

我们的类定义了一个等待时间,使模拟的用户在执行每个任务(见下文)后等待 1 到 5 秒。

使用 @task 装饰的方法是 locust 文件的核心。对于每个正在运行的用户,Locust 创建一个 greenlet(微线程),该线程将调用这些方法。

我们通过装饰两个方法 @task 声明了两个任务,其中一个给予了更高的权重(3)。当我们的 QuickstartUser 运行时,它会选择声明的任务之一——在本例中是 hello_worldview_items——并执行它。任务是随机选择的,但你可以给它们不同的权重。上面的配置将使 Locust 选择 view_items 的概率比选择 hello_world 的概率高3倍。任务执行完毕后,用户将在等待时间(在本例中为 1 到 5 秒)内休眠。在等待时间结束后,它将选择一个新的任务并不断重复执行。

请注意,只有使用 @task 装饰的方法才会被选择执行,因此您可以按照您自己的需要定义任何内部帮助程序方法。

self.client 属性使得可以进行HTTP调用并且将被 Locust 记录。有关如何进行其他类型的请求、验证响应等信息。

注意

HttpUser不是一个真正的浏览器,因此不会解析HTML响应来加载资源或呈现页面。但它会跟踪cookie。

在view_items任务中,我们使用一个可变的查询参数加载10个不同的URL。为了不在Locust的统计信息中获得10个单独的条目 - 因为统计信息是按URL分组的 - 我们使用名称参数将所有这些请求分组到一个名为“/item”的条目下。

此外,我们声明了一个on_start方法。当每个模拟用户启动时,将调用具有此名称的方法。

1、自动生成Locustfile 

你可以使用har2locust工具基于浏览器记录(HAR文件)生成Locustfile。

对于不习惯编写自己的Locustfile的初学者来说,这非常有用,但对于更高级的用例也可以高度自定义。

har2locust安装:

注意:har2locust仍处于beta版。它可能无法始终生成正确的Locustfile,并且其界面可能在版本之间发生更改。

2、用户类(User class)

用户类表示系统中一个类型的用户/场景。在进行测试运行时,您指定要模拟的并发用户数,Locust将为每个用户创建一个实例。您可以向这些类/实例添加任何属性,但是有一些属性对于Locust具有特殊的含义:

2.1 等待时间属性 

用户的wait_time方法可以在每个任务执行后轻松引入延迟。如果未指定wait_time,则下一个任务将在上一个任务完成后立即执行。

  • constant为固定的时间

  • between为最小和最大值之间的随机时间

例如,要使每个用户在每个任务执行之间等待0.5到10秒

  • constant_throughput属性,用于自适应时间,以确保任务(最多)每秒运行X次。

  • constant_pacing属性,用于自适应时间,以确保任务每X秒运行一次(它是constant_throughput的数学倒数)。

特别提示:

例如,如果您希望Locust在高峰负载时运行500个任务迭代/秒,则可以使用wait_time = constant_throughput(0.1)和5000个用户数。

等待时间只能限制吞吐量,而不能启动新的用户以达到目标。因此,在我们的例子中,如果任务迭代的时间超过10秒,则吞吐量将低于500。

等待时间应用于任务而不是请求。例如,如果您在任务中指定了wait_time = constant_throughput(2)并执行了两个请求,则您的请求速率/RPS将是每个用户4个。

还可以直接在类上声明自己的wait_time方法。例如,以下User类将睡眠一秒钟,然后是两秒钟,然后是三秒钟,等等。

2.2 weight和fixed_count属性

如果文件中存在多个用户类,并且没有在命令行上指定用户类,则Locust将生成相等数量的每个用户类的实例。您还可以通过将它们作为命令行参数传递到同一个Locustfile中来指定要使用哪些用户类:

如果您想模拟更多特定类型的用户,可以在这些类上设置一个权重属性。例如,Web用户比移动用户更有可能是三倍:

此外,您还可以设置fixed_count属性。在这种情况下,将忽略权重属性,并生成确切数量的用户。这些用户首先被生成。在下面的示例中,只会生成一个AdminUser实例,以便更精确地控制请求计数,独立于总用户计数。

2.3 host属性 

host属性是要加载的主机的URL前缀(即“http://huicewang.com”)。通常,在启动locust时,可以在Locust的Web UI或命令行上使用--host选项来指定。

如果在用户类中声明了host属性,则在命令行或Web请求中未指定--host时,将使用它。

2.4 tasks属性 

一个User类可以使用@task装饰器将任务声明为其下的方法,但也可以使用tasks属性来指定任务,这在下面将详细描述。

2.5 environment属性 

引用用户正在运行的环境。使用它来与环境或其包含的运行程序交互。例如,要从任务方法中停止运行程序:

如果在独立的locust实例上运行,则会停止整个运行。如果在工作节点上运行,则会停止该特定节点。

2.6 on_start和on_stop方法 

用户(和TaskSets)可以声明on_start方法和/或on_stop方法。当用户开始运行时,User将调用其on_start方法,并在停止运行时调用其on_stop方法。对于TaskSet,当模拟用户开始执行该TaskSet时,将调用on_start方法,并在停止执行该TaskSet(调用interrupt()或用户被终止)时调用on_stop。

3、任务(Tasks)

当一个负载测试开始时,每个模拟用户将创建一个 User 类的实例,并在自己的 green thread 中开始运行。当这些用户运行时,它们选择要执行的任务,然后等待一段时间,然后再选择一个新的任务,如此往复。

这些任务是普通的 Python 可调用函数,如果我们正在对拍卖网站进行负载测试,它们可以执行“加载起始页面”、“搜索某个产品”、“进行竞标”等操作。

3.1 @task 装饰器

为用户添加任务的最简单方法是使用 task 装饰器。

@task 可以带有一个可选的 weight 参数,用于指定任务的执行比例。在下面的例子中,task2 将被选中的概率是 task1 的两倍:

3.2 tasks 属性

定义用户任务的另一种方法是通过设置 tasks 属性。

tasks 属性可以是 Task 列表,也可以是 <Task : int> 字典,其中 Task 可以是 Python 可调用函数或 TaskSet 类。如果任务是普通的 Python 函数,它们会接收一个参数,即执行任务的 User 实例。

以下是将 User 任务声明为普通 Python 函数的示例:

如果 tasks 属性指定为列表,则每次要执行任务时,它将从 tasks 属性中随机选择。但是,如果 tasks 是一个字典,其中 callable 作为键,int 作为值,则将随机选择要执行的任务,但使用 int 作为比率。因此,对于如下任务:

my_task 将比 another_task 执行的概率高 3 倍。

在内部,上述字典实际上会被扩展成一个列表(并更新 tasks 属性),看起来像这样:

然后使用 Python 的 random.choice() 从列表中选择任务。

3.3 @tag 装饰器

通过使用 @tag 装饰器为任务打标签,您可以使用 --tags 和 --exclude-tags 参数选择在测试期间执行哪些任务。考虑以下示例:

如果您在启动测试时使用了 --tags tag1,则仅会在测试期间执行task1和task2。如果您使用了 --tags tag2 tag3,则仅会执行task2和task3。

--exclude-tags则完全相反。因此,如果您使用 --exclude-tags tag3 启动测试,则仅会执行task1、task2和task4。排除始终优先于包含,因此如果一个任务具有您包含和排除的标记,它将不会被执行。

4、事件 

如果您想要运行一些设置代码作为测试的一部分,通常将其放在locustfile的模块级别即可,但有时您需要在运行的特定时间执行某些操作。为此需求,Locust提供了事件钩子。

4.1 test_start和test_stop

如果您需要在负载测试的开始或结束时运行某些代码,您应该使用test_start和test_stop事件。您可以在locustfile的模块级别设置这些事件的监听器:

4.2 初始化(init)

初始化事件在每个Locust进程开始时触发。这在分布式模式下特别有用,因为每个工作进程(而不是每个用户)都需要有机会进行一些初始化。例如,假设您有一些全局状态,所有从该进程生成的用户都需要:

5、HttpUser 类 

HttpUser 是最常用的用户类。它添加了一个 client 属性,用于发起 HTTP 请求。

5.1 client 属性 / HttpSession

client 是 HttpSession 的一个实例。HttpSession 是 requests.Session 的一个子类/包装类,因此其特性都有很好的文档说明,应该对很多人来说都很熟悉。HttpSession 添加的主要功能是将请求结果报告给 Locust(成功/失败、响应时间、响应长度、名称)。

它包含所有 HTTP 方法的方法:get、post、put 等等。

就像 requests.Session 一样,它保留请求之间的 cookies,因此可以轻松地用于登录网站。

发起 POST 请求,查看响应并隐式地重用我们得到的任何会话 cookie 发起第二个请求

HttpSession 捕获 Session 抛出的任何 requests.RequestException(由连接错误、超时或类似问题引起),而不是返回 status_code 设置为 0 且内容设置为 None 的虚拟响应对象。

5.2 验证响应(Validating responses)

如果 HTTP 响应代码为 OK(<400),则认为请求成功,但通常还有必要对响应进行一些额外的验证。

您可以使用 catch_response 参数、with 语句和 response.failure() 调用将请求标记为失败。

即使响应代码是错误的,您也可以将请求标记为成功:

您甚至可以通过引发异常并在 with 块之外捕获它来避免记录请求。或者您可以抛出 locust 异常,如下面的示例所示,让 Locust 捕获它。

5.3 REST/JSON APIs

FastHttpUser提供了现成的rest方法,但您也可以自己实现:

5.4 分组请求(Grouping requests)

网站通常具有URL包含某种动态参数的页面。通常,将这些URL组合在一起以显示用户统计信息是有意义的。这可以通过将名称参数传递给HttpSession的不同请求方法来完成。

例如:

在与包装Requests会话的库/SDK交互时,可能存在无法将参数传递给请求函数的情况。通过设置client.request_name属性,提供了另一种分组请求的方法。

如果您想以最少的样板代码链接多个分组,请使用client.rename_request()上下文管理器。

使用catch_response并直接访问request_meta,甚至可以基于响应中的某些内容重命名请求。

5.5 HTTP代理设置(HTTP Proxy settings)

为了提高性能,我们将requests配置为不在环境中查找HTTP代理设置,方法是将requests.Session的trust_env属性设置为False。如果您不希望这样做,您可以手动将locust_instance.client.trust_env设置为True。有关详细信息,请参阅requests的文档。

5.6 连接池(Connection pooling)

由于每个HttpUser都会创建新的HttpSession,因此每个用户实例都有自己的连接池。这类似于真实用户与Web服务器的交互方式。

但是,如果要在所有用户之间共享连接,则可以使用单个池管理器。为此,请将pool_manager类属性设置为urllib3.PoolManager的实例。

6、TaskSets

TaskSets是一种结构化测试层次式Web站点/系统的方式。您可以在此处阅读更多信息。

示例

这里有很多locustfile的示例

如何构造您的测试代码

重要的是要记住,locustfile.py只是一个普通的Python模块,由Locust导入。从此模块中,您可以像在任何Python程序中一样自由地导入其他Python代码。当前工作目录会自动添加到python的sys.path中,因此可以使用python import语句导入驻留在工作目录中的任何python文件/模块/软件包。

对于小型测试,将所有测试代码放在单个locustfile.py中应该可以正常工作,但对于较大的测试套件,您可能需要将代码拆分为多个文件和目录。

您如何结构化测试源代码当然完全取决于您,但我们建议您遵循Python最佳实践。这是一个想象中的Locust项目的示例文件结构:

项目文件结构

具有多个locustfiles的项目也可以将它们保存在单独的子目录中:

项目文件结构

在任何上述项目结构中,您的locustfile都可以使用以下方式导入公共库:


Locust核心知识详解的评论 (共 条)

分享到微博请遵守国家法律