转载于:https://www.cnblogs.com/codeobj/p/13618305.html
1、JMeter和Locust的对比说明
1)开源许可证
性能测试-locust简介及使用
工具许可范围的问题是最重要的问题之一,因为您可能想知道是否需要支付额外的第三方工具来完成负载测试。 如果某个工具是开源的,那么您几乎可以实现为性能测试设置的任何目标,而无需任何额外付款。 开源JMeter和Locust也不例外。
JMeter和Locust都提供了许可软件许可证,该许可证支持免费软件,对软件的分发方式提出最低要求。 JMeter是由Apache开发的,它基于Apache License 2.0,而Locust是由一个由社区驱动的开发人员组成的小团队开发的 ,基于MIT许可证。 在这两种情况下,这些工具都是开源的,允许您自由使用它们,而不受任何使用限制。
2)负载测试创建和维护
性能测试工作流程有三个主要步骤:创建,运行和分析。 一般第一步是最耗时的。
编写JMeter性能测试的最常用方法是使用其GUI模式。 JMeter GUI模式提供了一个桌面客户端,允许您轻松创建测试,而无需编写单行代码(直到您需要创建棘手的测试)。 所以最简单的场景可能如下所示:
JMeter非常简单,通常,即使是没有经验的工程师也可以毫无困难地上手。但是如果需要,您可以使用Java在GUI和非GUI模式下使用代码。 但是,由于脚本实现的复杂性(因为JMeter旨在与GUI模式一起使用)以及缺乏如何制作此类脚本的文档,因此这种方式在JMeter社区中并不流行。
Locust则需要python编程基础。
3)支持的协议
理想情况下,您应该能够使用尽可能少的工具测试所有工具,只要它不会影响测试质量。
使用JMeter,您可以使用完整的内置函数和第三方插件,在一个地方创建所有内容的性能测试。 您无需编码即可测试不同的协议甚至数据库。 这些包括JDBC,FTP,LDAP,SMTP等。JMeter还可以通过jar包扩展,比如加载jython,可以使用python脚本。
根据文档,Locust主要用于基于HTTP Web的测试。但可以扩展其默认功能并创建自定义Python函数来测试可以使用Python编程语言进行测试的任何内容。
4)并发用户数
JMeter和Locust有完全不同的方式来处理机器资源。 JMeter有一个基于线程的模型,它为每个用户分配一个单独的线程。 每个步骤的线程分配和基准测试需要大量资源,这就是为什么JMeter对于您可以在一台机器上模拟的用户数量非常有限的原因。 您可以在一台计算机上运行的用户数取决于许多因素,如脚本复杂性,硬件,响应大小等。 如果您的脚本很简单,JMeter允许您在一台机器上运行多达数千个,但脚本执行逐渐变得不可靠。
Locust有完全不同的用户模拟模型,它基于事件和异步方法(协程),以gevent coroutine作为整个过程的基石。 这种实现允许Locust框架在一台机器上轻松模拟数千个并发用户,即使是在非常规的笔记本电脑上,也可同时运行内部有许多步骤的复杂测试。
5)增强灵活性
这两个工具提供相对相同的生成负载的方式 - 您可以指定在性能测试期间要使用的用户数以及它们应该加速的速度。
在JMeter中,您可以在指定字段的“线程组”控制器中配置负载:但是JMeter还有其他插件,可以让您配置非常灵活的负载。 最好的方法之一是使用Ultimate Thread Group ,它允许用户制作非常具体的加载模式:
Locust有不同的方法。 当您运行性能脚本时,Locust会自动在http://localhost:8089上启动Web界面的服务器,该界面为您提供仅指定线性负载的输入元素, 当然也可以命令行执行通过参数定制。
6)脚本录制
这是JMeter具有强大优势的地方,因为它具有脚本录制的内置功能,而Locust根本没有此功能。 除此之外,还有许多第三方插件可以为JMeter制作脚本录制。 记录此类脚本最方便的方法之一是使用BlazeMeter chrome扩展。
7)测试监控
JMeter和Locust都提供了强大的内置功能来监控性能脚本并分析您的测试结果。 JMeter有许多不同的元素叫做监听器。 每个侦听器都提供特定类型的监视,你也可以使用许多现有的自定义监听器扩展默认库。另一方面,JMeter监听器在其运行的机器上消耗大量资源。这就是为什么通常,JMeter是以非GUI模式执行的,没有任何监听器或监控过程,在这种情况下,可使用3方工具,如BlazeMeter 。
Locust的监测能力稍弱,不过几乎提供了所有可用于监控基本负载的信息。在脚本运行期间,Locust运行一个简单的Web服务器,
2、编写Locust示例代码
locust_sample.py
from locust import HttpUser, TaskSet, task
class WebsiteTasks(TaskSet):
def on_start(self):
self.client.post("/login", { "username": "test", "password": "123456" })
@task(2)
def index(self):
self.client.get("/")
@task(1)
def about(self):
self.client.get("/about/")
tasks = {index: 2, about: 1} # 与装饰器效果一致
class WebsiteUser(HttpUser):
# task_set = WebsiteTasks # Usage of User.task_set is deprecated since version 1.0. Set the tasks attribute instead (tasks = [WebsiteTasks])
tasks = [WebsiteTasks]
host = "http://debugtalk.com"
min_wait = 1000
max_wait = 5000
启动测试:locust -H http://debugtalk.com -f locust_sample.py
3、Locust类详细讲解
在Locust类中,具有一个client属性,它对应着虚拟用户作为客户端所具备的请求能力,也就是我们常说的请求方法。通常情况下,我们不会直接使用Locust类,因为其client属性没有绑定任
何方法。因此在使用Locust时,需要先继承Locust类,然后在继承子类中的client属性中绑定客户端的实现类。对于常见的HTTP(S)协议,Locust已经实现了HttpUser(1.0之前使用HttpLocust)类,其client属性绑
定了HttpSession类,而HttpSession又继承自requests.Session。因此在测试HTTP(S)的Locust脚本中,我们可以通过client属性来使用Python requests库的所有方法,包括
GET/POST/HEAD/PUT/DELETE/PATCH等,调用方式也与requests完全一致。另外,由于requests.Session的使用,因此client的方法调用之间就自动具有了状态记忆的功能。常见的场景
就是,在登录系统后可以维持登录状态的Session,从而后续HTTP请求操作都能带上登录态。而对于HTTP(S)以外的协议,我们同样可以使用Locust进行测试,只是需要我们自行实现客户端
。在客户端的具体实现上,可通过注册事件的方式,在请求成功时触发events.request_success,在请求失败时触发events.request_failure即可。然后创建一个继承自Locust类的类,对其
设置一个client属性并与我们实现的客户端进行绑定。后续,我们就可以像使用HttpUser类一样,测试其它协议类型的系统。
原理就是这样简单!
在Locust类中,除了client属性,还有几个属性需要关注下:
-
tasks(1.0以下是task_set): tasks = [WebsiteTasks] Collection of python callables and/or TaskSet classes that the Locust user(s) will run.指向一个TaskSet类的列表,TaskSet类定义了用户的任务信息,该属性为必填;**
-
max_wait/min_wait: 每个用户执行两个任务间隔时间的上下限(毫秒),具体数值在上下限中随机取值,若不指定则默认间隔时间固定为1秒;
-
host:被测系统的host,当在终端中启动locust时没有指定--host参数时才会用到;
-
weight:同时运行多个Locust类时会用到,用于控制不同类型任务的执行权重。
测试开始后,每个虚拟用户(Locust实例)的运行逻辑都会遵循如下规律:
-
先执行WebsiteTasks中的on_start(只执行一次),作为初始化;
-
从WebsiteTasks中随机挑选(如果定义了任务间的权重关系,那么就是按照权重关系随机挑选)一个任务执行;
-
根据Locust类中min_wait和max_wait定义的间隔时间范围(如果TaskSet类中也定义了min_wait或者max_wait,以TaskSet中的优先),在时间范围中随机取一个值,休眠等待;
-
重复2~3步骤,直至测试任务终止。
4、TaskSet类详细讲解
性能测试工具要模拟用户的业务操作,就需要通过脚本模拟用户的行为。在前面的比喻中说到,TaskSet类好比蝗虫的大
脑,控制着蝗虫的具体行为。
具体地,TaskSet类实现了虚拟用户所执行任务的调度算法,包括规划任务执行顺序(schedule_task)、挑选下一个任
务(execute_next_task)、执行任务(execute_task)、休眠等待(wait)、中断控制(interrupt)等等。在此基
础上,我们就可以在TaskSet子类中采用非常简洁的方式来描述虚拟用户的业务测试场景,对虚拟用户的所有行为(任务
)进行组织和描述,并可以对不同任务的权重进行配置。
在TaskSet子类中定义任务信息时,可以采取两种方式,@task装饰器和tasks属性。
采用@task装饰器定义任务信息时,描述形式如下:
class WebsiteTasks(TaskSet):
def on_start(self):
self.client.post("/login", { "username": "test", "password": "123456" })
@task(2)
def index(self):
self.client.get("/")
@task(1)
def about(self):
self.client.get("/about/")
采用tasks属性定义任务信息时,描述形式如下:
class WebsiteTasks(TaskSet):
def on_start(self):
self.client.post("/login", { "username": "test", "password": "123456" })
# @task(2)
def index(self):
self.client.get("/")
# @task(1)
def about(self):
self.client.get("/about/")
tasks = {index: 2, about: 1} # 与装饰器效果一致
在TaskSet子类中除了定义任务信息,还有一个是经常用到的,那就是on_start函数。这个和LoadRunner中的vuser_init功能相同,在正式执行测试前执行一
次,主要用于完成一些初始化的工作。例如,当测试某个搜索功能,而该搜索功能又要求必须为登录态的时候,就可以先在on_start中进行登录操作;前面也提 到,HttpUser使用到了requests.Session,因此后续所有任务执行过程中就都具有登录态了。
5、实战测试
1)测试代码
from locust import HttpUser, TaskSet, task
class WebsiteTasks(TaskSet):
def on_start(self):
# self.client.post("/login", { "username": "test", "password": "123456" })
self.client.get("/login?key=00d91e8e0cca2b76f515926a36db68f5&phone=13594347817&passwd=123456")
# @task(2)
def videoCategory(self):
self.client.get("/videoCategory")
# @task(1)
def videoRecommend(self):
self.client.get("/videoRecommend?id=127398")
def todayVideo(self):
self.client.get("/todayVideo")
def getJoke(self):
self.client.get("/getJoke?page=1&count=2&type=video")
def novelSearchApi(self):
self.client.get("/searchPoetry?name=古风二首%20二")
tasks = {videoCategory: 2, videoRecommend: 1, todayVideo: 2, getJoke: 3, novelSearchApi: 2} # 与装饰器效果一致
class WebsiteUser(HttpUser):
# https://blog.csdn.net/c__chao/article/details/78573737
# task_set = WebsiteTasks # Usage of User.task_set is deprecated since version 1.0. Set the tasks attribute instead (tasks = [WebsiteTasks])
tasks = [WebsiteTasks]
host = "https://api.apiopen.top"
min_wait = 1000
max_wait = 5000