locust===Writing a locustfile
The Locust class
A locust class represents one user (or a swarming locust if you will). Locust will spawn (hatch) one instance of the locust class for each user that is being simulated. There are a few attributes that a locust class should typically define.
The task_set
attribute
The task_set
attribute should point to a TaskSet
class which defines the behaviour of the user and is described in more detail below.
The min_wait and max_wait attributes
In addition to the task_set attribute, one usually wants to declare the min_wait and max_wait attributes. These are the minimum and maximum time respectively, in milliseconds, that a simulated user will wait between executing each task. min_wait and max_wait default to 1000, and therefore a locust will always wait 1 second between each task if min_wait and max_wait are not declared.
With the following locustfile, each user would wait between 5 and 15 seconds between tasks:
from locust import Locust, TaskSet, task class MyTaskSet(TaskSet):
@task
def my_task(self):
print "executing my_task" class MyLocust(Locust):
task_set = MyTaskSet
min_wait = 5000
max_wait = 15000
The min_wait and max_wait attributes can also be overridden in a TaskSet class.
The weight attribute
You can run two locusts from the same file like so:
locust -f locust_file.py WebUserLocust MobileUserLocust
If you wish to make one of these locusts execute more often you can set a weight attribute on those classes. Say for example, web users are three times more likely than mobile users:
class WebUserLocust(Locust):
weight = 3
.... class MobileUserLocust(Locust):
weight = 1
....
TaskSet class
If the Locust class represents a swarming locust, you could say that the TaskSet class represents the brain of the locust. Each Locust class must have a task_set attribute set, that points to a TaskSet.
A TaskSet is, like its name suggests, a collection of tasks. These tasks are normal python callables and—if we were load-testing an auction website—could do stuff like “loading the start page”, “searching for some product” and “making a bid”.
When a load test is started, each instance of the spawned Locust classes will start executing their TaskSet. What happens then is that each TaskSet will pick one of its tasks and call it. It will then wait a number of milliseconds, chosen at random between the Locust class’ min_wait and max_wait attributes (unless min_wait/max_wait have been defined directly under the TaskSet, in which case it will use its own values instead). Then it will again pick a new task to be called, wait again, and so on.
Declaring tasks
The typical way of declaring tasks for a TaskSet it to use the task
decorator.
Here is an example:
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 takes an optional weight argument that can be used to specify the task’s execution ratio. In the following example task2 will be executed twice as much as task1:
class MyTaskSet(TaskSet):
min_wait = 5000
max_wait = 15000 @task(3)
def task1(self):
pass @task(6)
def task2(self):
pass class MyLocust(Locust):
task_set = MyTaskSet
tasks attribute
Using the @task decorator to declare tasks is a convenience, and usually the best way to do it. However, it’s also possible to define the tasks of a TaskSet by setting the tasks
attribute (using the @task decorator will actually just populate the tasks attribute).
The tasks attribute is either a list of python callables, or a <callable : int> dict. The tasks are python callables that receive one argument—the TaskSet class instance that is executing the task. Here is an extremely simple example of a locustfile (this locustfile won’t actually load test anything):
from locust import Locust, TaskSet def my_task(l):
pass class MyTaskSet(TaskSet):
tasks = [my_task] class MyLocust(Locust):
task_set = MyTaskSet
If the tasks attribute is specified as a list, each time a task is to be performed, it will be randomly chosen from the tasks attribute. If however, tasks is a dict—with callables as keys and ints as values—the task that is to be executed will be chosen at random but with the int as ratio. So with a tasks that looks like this:
{my_task: 3, another_task:1}
my_task would be 3 times more likely to be executed than another_task.
TaskSets can be nested
A very important property of TaskSets is that they can be nested, because real websites are usually built up in an hierarchical way, with multiple sub-sections. Nesting TaskSets will therefore allow us to define a behaviour that simulates users in a more realistic way. For example we could define TaskSets with the following structure:
- Main user behaviour
- Index page
- Forum page
- Read thread
- Reply
- New thread
- View next page
- Browse categories
- Watch movie
- Filter movies
- About page
The way you nest TaskSets is just like when you specify a task using the tasks attribute, but instead of referring to a python function, you refer to another TaskSet:
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
So in the above example, if the ForumPage would get selected for execution when the UserBehaviour TaskSet is executing, then the ForumPage TaskSet would start executing. The ForumPage TaskSet would then pick one of its own tasks, execute it, wait, and so on.
There is one important thing to note about the above example, and that is the call to self.interrupt() in the ForumPage’s stop method. What this does is essentially to stop executing the ForumPage task set and the execution will continue in the UserBehaviour instance. If we didn’t have a call to the interrupt()
method somewhere in ForumPage, the Locust would never stop running the ForumPage task once it has started. But by having the interrupt function, we can—together with task weighting—define how likely it is that a simulated user leaves the forum.
It’s also possible to declare a nested TaskSet, inline in a class, using the @task
decorator, just like when declaring normal tasks:
class MyTaskSet(TaskSet):
@task
class SubTaskSet(TaskSet):
@task
def my_task(self):
pass
The on_start function
A TaskSet class can optionally declare an on_start
function. If so, that function is called when a simulated user starts executing that TaskSet class.
Referencing the Locust instance, or the parent TaskSet instance
A TaskSet instance will have the attribute locust
point to its Locust instance, and the attribute parent
point to its parent TaskSet (it will point to the Locust instance, in the base TaskSet).
Making HTTP requests
So far, we’ve only covered the task scheduling part of a Locust user. In order to actually load test a system we need to make HTTP requests. To help us do this, the HttpLocust
class exists. When using this class, each instance gets a client
attribute which will be an instance of HttpSession
which can be used to make HTTP requests.
- class
HttpLocust
-
Represents an HTTP “user” which is to be hatched and attack the system that is to be load tested.
The behaviour of this user is defined by the task_set attribute, which should point to a
TaskSet
class.This class creates a client attribute on instantiation which is an HTTP client with support for keeping a user session between requests.
client
= None-
Instance of HttpSession that is created upon instantiation of Locust. The client support cookies, and therefore keeps the session between HTTP requests.
When inheriting from the HttpLocust class, we can use its client attribute to make HTTP requests against the server. Here is an example of a locust file that can be used to load test a site with two URLs; / and /about/:
from locust import HttpLocust, TaskSet, task 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
min_wait = 5000
max_wait = 15000
Using the above Locust class, each simulated user will wait between 5 and 15 seconds between the requests, and / will be requested twice as much as /about/.
The attentive reader will find it odd that we can reference the HttpSession instance using self.client inside the TaskSet, and not self.locust.client. We can do this because the TaskSet
class has a convenience property called client that simply returns self.locust.client.
Using the HTTP client
Each instance of HttpLocust has an instance of HttpSession
in the client attribute. The HttpSession class is actually a subclass of requests.Session
and can be used to make HTTP requests, that will be reported to Locust’s statistics, using the get
, post
, put
, delete
, head
, patch
and options
methods. The HttpSession instance will preserve cookies between requests so that it can be used to log in to websites and keep a session between requests. The client attribute can also be referenced from the Locust instance’s TaskSet instances so that it’s easy to retrieve the client and make HTTP requests from within your tasks.
Here’s a simple example that makes a GET request to the /about path (in this case we assume self is an instance of a TaskSet
or HttpLocust
class:
response = self.client.get("/about")
print "Response status code:", response.status_code
print "Response content:", response.content
And here’s an example making a POST request:
response = self.client.post("/login", {"username":"testuser", "password":"secret"})
Safe mode
The HTTP client is configured to run in safe_mode. What this does is that any request that fails due to a connection error, timeout, or similar will not raise an exception, but rather return an empty dummy Response object. The request will be reported as a failure in Locust’s statistics. The returned dummy Response’s content attribute will be set to None, and its status_code will be 0.
Manually controlling if a request should be considered successful or a failure
By default, requests are marked as failed requests unless the HTTP response code is OK (2xx). Most of the time, this default is what you want. Sometimes however—for example when testing a URL endpoint that you expect to return 404, or testing a badly designed system that might return 200 OK even though an error occurred—there’s a need for manually controlling if locust should consider a request as a success or a failure.
One can mark requests as failed, even when the response code is OK, by using the catch_response argument and a with statement:
with client.get("/", catch_response=True) as response:
if response.content != "Success":
response.failure("Got wrong response")
Just as one can mark requests with OK response codes as failures, one can also use catch_response argument together with a with statement to make requests that resulted in an HTTP error code still be reported as a success in the statistics:
with client.get("/does_not_exist/", catch_response=True) as response:
if response.status_code == 404:
response.success()
Grouping requests to URLs with dynamic parameters
It’s very common for websites to have pages whose URLs contain some kind of dynamic parameter(s). Often it makes sense to group these URLs together in Locust’s statistics. This can be done by passing a name argument to the HttpSession's
different request methods.
Example:
# Statistics for these requests will be grouped under: /blog/?id=[id]
for i in range(10):
client.get("/blog?id=%i" % i, name="/blog?id=[id]")
Common libraries
Often, people wish to group multiple locustfiles that share common libraries. In that case, it is important to define the project root to be the directory where you invoke locust, and it is suggested that all locustfiles live somewhere beneath the project root.
A flat file structure works out of the box:
- project root
commonlib_config.py
commonlib_auth.py
locustfile_web_app.py
locustfile_api.py
locustfile_ecommerce.py
The locustfiles may import common libraries using, e.g. import commonlib_auth
. This approach does not cleanly separate common libraries from locust files, however.
Subdirectories can be a cleaner approach (see example below), but locust will only import modules relative to the directory in which the running locustfile is placed. If you wish to import from your project root (i.e. the location where you are running the locust command), make sure to write sys.path.append(os.getcwd())
in your locust file(s) before importing any common libraries—this will make the project root (i.e. the current working directory) importable.
- project root
__init__.py
common/
__init__.py
config.py
auth.py
locustfiles/
__init__.py
web_app.py
api.py
ecommerce.py
With the above project structure, your locust files can import common libraries using:
sys.path.append(os.getcwd())
import common.auth
*from http://docs.locust.io/en/latest/writing-a-locustfile.html#the-locust-class
locust===Writing a locustfile的更多相关文章
- Locust:简介和基本用法
我个人在性能测试工作中,负载生成工具使用的大多都是jmeter,之前学习python时顺带了解过python开源的性能测试框架locust. 这篇博客,简单介绍下locust的使用方法,仅供参考... ...
- Locust分布式负载测试工具入门
忽略元数据末尾 回到原数据开始处 Locust简介 Locust是一个简单易用的分布式负载测试工具,主要用来对网站进行负载压力测试. 以下是github上的仓库地址 https://github.co ...
- 面向Web应用的并发压力测试工具——Locust实用攻略
1. 概述 该方案写作目的在于描述一个基于Locust实现的压力测试,文中详细地描述了如何利用locustfile.py文件定义期望达成的测试用例,并利用Locust对目标站点进行并发压力测试. 特别 ...
- httprunner3源码解读(1)简单介绍源码模块内容
前言 最近想着搭建一个API测试平台,基础的注册登录功能已经完成,就差测试框架的选型,最后还是选择了httprunner,github上已经有很多开源的httprunner测试平台,但是看了下都是基于 ...
- locust===官方说明文档,关于tasks
安装: >>> pip install locust locust在官方simple_code中如下: from locust import HttpLocust, TaskSet ...
- 性能测试框架Locust初学笔记
Locust初探 Locust是一款类似于Jmeter开源负载测试工具,所不同的是它是用python实现,并支持python脚本. locust提供web ui界面,能够方便用户实时监控脚本运行状态. ...
- Locust性能测试工具的安装及实际应用
一.安装Locust 安装Locust之前先安装的库:gevent库:第三方库,gevent为python提供了比较完善的协程支持.使用gevent,可以获得极高的并发性能. pip install ...
- Locust性能测试框架,从入门到精通
1. Locust简介 Locust是使用Python语言编写实现的开源性能测试工具,简洁.轻量.高效,并发机制基于gevent协程,可以实现单机模拟生成较高的并发压力. 主要特点如下: 使用普通的P ...
- Locust no-web 模式与参数详解
读前参考:<性能测试工具Locust > 熟悉 Apache ab 工具的同学都知道,它是没有界面的,通过命令行执行. Locust 同样也提供的命令行运行,好处就是更节省客户端资源. 命 ...
随机推荐
- 第四篇 Python循环
While 循环 For 循环
- CentOs 版本名字说明
What images are in this directory CentOS-6.3-x86_64-netinstall.iso This is the network install and r ...
- Django数据模型--表关系(一对多)
一.一对一关系 使用方法:models.ForeignKey(要关联的模型) 举例说明:年级.教师和学生 from django.db import models class Grade(models ...
- Android之内容提供者ContentProvider的总结
本文包含以下知识点: ContentProvider Uri 的介绍 ContentResolver: 监听ContentProvider的数据改变 一:ContentProvider部分 Conte ...
- NO2——最短路径
[Dijkstra算法] 复杂度O(n2) 权值必须非负 /* 求出点beg到所有点的最短路径 */ // 邻接矩阵形式 // n:图的顶点数 // cost[][]:邻接矩阵 // pre[i]记录 ...
- Python模块学习:logging 日志记录
原文出处: DarkBull 许多应用程序中都会有日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系统的运行状况进行跟踪.在.NET平台中,有非常著名的第三方开源日志组件log4net ...
- android仿QQ的SlideMenu
这其实很简单就可以实现,只需要自定义一个View继承自HorizontalScrollView 1,新建一个项目,再新建一个MySlideMenu继承HorizontalScrollView publ ...
- Name node is in safe mode.
刚才启动hadoop,然后执行rm -r命令,出现这个问题,标记为红色的部分意思是namenode是安全节点, [master@hadoop file]$ hadoop fs -rm -r /inp ...
- vue-cli配置jquery 以及jquery第三方插件
只使用jquery: 1. cnpm install jquery --save 2. cnpm install @types/jquery --save-dev (不使用ts的不需要安装此声明 ...
- 算法(8)Maximum Product Subarray
题目:在一个数组中找到一个子数组,让子数组的乘积最大,比如[2,3,-2,4]返回6 思路:之前自己想到的思路是对于一个int类型的数组,只要负数的个数是偶数,那么乘积肯定是全局乘就可以了,然后对于负 ...