Locust是什么?

Locust 是一个开源负载测试工具。使用 Python 代码定义用户行为,也可以仿真百万个用户。
Locust 是非常简单易用,分布式,用户负载测试工具。Locust 主要为网站或者其他系统进行负载测试,能测试出一个系统可以并发处理多少用户。
Locust 是完全基于时间的,因此单个机器支持几千个并发用户。相比其他许多事件驱动的应用,Locust 不使用回调,而是使用轻量级的处理方式 gevent

安装

  1. pip install locustio

如果你打算通过多进程或多机,分布式地运行Locust,我们建议你也安装pyzmq

  1. pip install pyzmq

TaskSet类

每个Locust类必须有一个指向一个TaskSettask_set属性。
TaskSet是任务的集合,这些任务是普通的python可调用对象。
当负载测试启动的时候,产生的每一个Locust类的实例都会开始执行它们的TaskSet。接下来发生的是,每个TaskSet会选择它的任务中的一个,并且调用它。接下来等待min_waitmax_wait毫秒,然后它会再选择下一个要被调用的任务,再等待,等等。

声明任务

为TaskSet声明任务最典型的方式是使用task装饰器。
下面是一个例子:

  1. def index(l):
  2. l.client.get("/")
  3. def stats(l):
  4. l.client.get("/stats/requests")
  5. class UserTasks(TaskSet):
  6. # one can specify tasks like this
  7. tasks = [index, stats]
  8. # but it might be convenient to use the @task decorator
  9. @task(3)
  10. def task1(self):
  11. self.client.get("/does_not_exist")
  12. @task(6)
  13. def task2(self):
  14. pass
  15. class WebsiteUser(HttpLocust):
  16. """
  17. Locust user class that does requests to the locust web server running on localhost
  18. """
  19. host = "http://127.0.0.1:8089"
  20. min_wait = 2000
  21. max_wait = 5000
  22. task_set = UserTasks
  • @task装饰器带一个可选的weight参数,它用于指定任务的执行比例。在下面的例子中,task2的执行次数是task1的两倍
  • TaskSet支持嵌套
  1. class MyTaskSet(TaskSet):
  2. @task
  3. class SubTaskSet(TaskSet):
  4. @task
  5. def my_task(self):
  6. pass
  • on_start函数
    TaskSet类可以定义一个on_start方法,当模拟用户开始执行TaskSet类的时候,on_start方法会被调用。

    HttpLocust类

    本文已经包含了Locust用户的任务调度部分,为了真正的给一个系统进行负载测试,我们需要生成HTTP请求,HttpLocust类的存在,就是为了解决这个问题。当使用HttpLocust类的时候,每个实例都有一个client属性—-它是能够用于生成HTTP请求的HttpSession类的实例
    1. from locust import HttpLocust, TaskSet, task
    2. class MyTaskSet(TaskSet):
    3. @task(2)
    4. def index(self):
    5. self.client.get("/")
    6. @task(1)
    7. def about(self):
    8. self.client.get("/about/")
    9. class MyLocust(HttpLocust):
    10. task_set = MyTaskSet
    11. min_wait = 5000
    12. max_wait = 15000
    1. 使用上面的Locust类,每个模拟用户在请求之间都会等待5-15秒,并且/的请求次数是/about/的两倍。
  1. 用心的读者可能会觉得很奇怪:在TaskSet内部我们使用self.client而非self.locust.client开引用HttpSession实例,我们能这么做是因为:TaskSet类有一个便捷的被称作client的属性,它简单的返回self.locust.client
  • 生成GET请求的例子
  1. response = self.client.get("/about")
  2. print "Response status code:", response.status_code
  3. print "Response content:", response.content
  • 生成POST请求的例子
  1. response = self.client.post("/login", {"username":"testuser", "password":"secret"})
  • 人工控制一个请求被视为成功还是失败

默认情况下,除非HTTP响应码是ok(2xx),否则请求就会被标记为失败。大多数情况下,默认的情况就是我们想要的。然而有时:比如说你期望返回404,或者是测试一个即使发生错误,仍然返回200 OK的系统,就存在人工控制locust将请求视为成功还是失败的需求。 通过使用catch_response参数和with语句,可以把一个响应码是okay的请求标记成失败

  1. with client.get("/", catch_response=True) as response:
  2. if response.content != "Success":
  3. response.failure("Got wrong response")

正如可以把响应码为OK的请求标记为失败,也可以使用catch_response参数和with语句,将返回http错误代码的请求在统计中报告为成功。

  1. with client.get("/does_not_exist/", catch_response=True) as response:
  2. if response.status_code == 404:
  3. response.success()
  • 安全模式

HTTP客户端被配置成以安全模式运行,任何由于连接错误,超时之类导致失败的请求都不会抛出异常,而是返回一个空的虚拟的Response对象,在Locust的统计中请求会被报告为一个失败。被返回的虚拟的Response对象的content属性被设置为None,status_code属性被设置为0

  • 将到具有动态参数的URL的请求分组

对于网站来说,拥有URL中包含某种动态参数的页面是非常普遍的。通常在Locust的统计中,把这些URL分成一组是有意义的。可以通过给HttpSession实例的请求方法传递name参数,来完成这件事。
例子:

  1. # Statistics for these requests will be grouped under: /blog/?id=[id]
  2. for i in range(10):
  3. client.get("/blog?id=%i" % i, name="/blog?id=[id]")

启动

  • 如果运行的文件名称为locustfile.py 则可以直接运行
  1. locust --host=http://example.com
  • 如果locust file被放到了其他的地方,我们可以运行
  1. locust -f 当前文件夹路径/my_file.py --host=http://example.com
  • 如果需要多线程分布式运行locust,启动的时候需要指定masterslave
  1. locust -f 当前文件夹路径/my_file.py --host=http://example.com --master

然后我们可以启动任意数量的slave进程:(用master机器的ip替换192.168.0.14)

  1. locust -f my_locustfile.py --slave --master-host=192.168.0.14

分布式运行Locust

参数

属性 解释
-f 文件名
--host 运行的接口的host
--master 以master的模式运行locust,web接口会运行在这个节点上
--slave 以slave模式运行locust。
--master-host=X.X.X.X --slave一起使用,用来设置master节点的ip或主机名(默认是127.0.0.1)
--master-port=5557 --slave一起使用,用来设置master节点的端口号(默认是5557),注意locust既会使用指定的端口号,又会使用指定的端口号+1,因此如果设置为5557,那么locust既会使用5557,也会使用5558
--master-bind-host=X.X.X.X --master一起使用,决定master节点绑定到哪一个网络接口,默认是*(所有可用的网络接口)

min_wait和max_wait属性

除了task_set属性,也可以声明min_waitmax_wait属性,它们是一个模拟用户在执行任务之间等待的最大和最小时间,单位是毫秒min_wait和max_wait默认是1000,因此如果没有声明min_waitmax_waitlocust在执行每个任务之间总是会等待1秒。
使用下面的locustfile,在任务之间每个用户等待5-15秒:

  1. from locust import Locust, TaskSet, task
  2. class MyTaskSet(TaskSet):
  3. @task
  4. def my_task(self):
  5. print "executing my_task"
  6. class MyLocust(Locust):
  7. task_set = MyTaskSet
  8. min_wait = 5000
  9. max_wait = 15000