深入浅出开源性能测试工具 Locust (使用篇 1)
在《【LocustPlus序】漫谈服务端性能测试》中,我对服务端性能测试的基础概念和性能测试工具的基本原理进行了介绍,并且重点推荐了Locust
这一款开源性能测试工具。然而,当前在网络上针对Locust
的教程极少,不管是中文还是英文,基本都是介绍安装方法和简单的测试案例演示,但对于较复杂测试场景的案例演示却基本没有,因此很多测试人员都感觉难以将Locust
应用到实际的性能测试工作当中。
经过一段时间的摸索,包括通读Locust
官方文档和项目源码,并且在多个性能测试项目中对Locust
进行应用实践,事实证明,Locust
完全能满足日常的性能测试需求,LoadRunner
能实现的功能Locust
也基本都能实现。
本文将从Locust
的功能特性出发,结合实例对Locust
的使用方法进行介绍。考虑到大众普遍对LoadRunner
比较熟悉,在讲解Locust
时也会采用LoadRunner
的一些概念进行类比。
概述
先从Locust
的名字说起。Locust
的原意是蝗虫,原作者之所以选择这个名字,估计也是听过这么一句俗语,“蝗虫过境,寸草不生”。我在网上找了张图片,大家可以感受下。
而Locust
工具生成的并发请求就跟一大群蝗虫一般,对我们的被测系统发起×××,以此检测系统在高并发压力下是否能正常运转。
在《【LocustPlus序】漫谈服务端性能测试》中说过,服务端性能测试工具最核心的部分是压力发生器,而压力发生器的核心要点有两个,一是真实模拟用户操作,二是模拟有效并发。
在Locust
测试框架中,测试场景是采用纯Python脚本进行描述的。对于最常见的HTTP(S)
协议的系统,Locust
采用Python的requests
库作为客户端,使得脚本编写大大简化,富有表现力的同时且极具美感。而对于其它协议类型的系统,Locust
也提供了接口,只要我们能采用Python编写对应的请求客户端,就能方便地采用Locust
实现压力测试。从这个角度来说,Locust
可以用于压测任意类型的系统。
在模拟有效并发方面,Locust
的优势在于其摒弃了进程和线程,完全基于事件驱动,使用gevent
提供的非阻塞IO
和coroutine
来实现网络层的并发请求,因此即使是单台压力机也能产生数千并发请求数;再加上对分布式运行的支持,理论上来说,Locust
能在使用较少压力机的前提下支持极高并发数的测试。
脚本编写
编写Locust
脚本,是使用Locust
的第一步,也是最为重要的一步。
简单示例
先来看一个最简单的示例。
from locust import HttpLocust, TaskSet, taskclass 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/")class WebsiteUser(HttpLocust):task_set = WebsiteTaskshost = "http://debugtalk.com"min_wait = 1000max_wait = 5000
在这个示例中,定义了针对http://debugtalk.com
网站的测试场景:先模拟用户登录系统,然后随机地访问首页(/
)和关于页面(/about/
),请求比例为2:1
;并且,在测试过程中,两次请求的间隔时间为1~5
秒间的随机值。
那么,如上Python脚本是如何表达出以上测试场景的呢?
从脚本中可以看出,脚本主要包含两个类,一个是WebsiteUser
(继承自HttpLocust
,而HttpLocust
继承自Locust
),另一个是WebsiteTasks
(继承自TaskSet
)。事实上,在Locust
的测试脚本中,所有业务测试场景都是在Locust
和TaskSet
两个类的继承子类中进行描述的。
那如何理解Locust
和TaskSet
这两个类呢?
简单地说,Locust类
就好比是一群蝗虫,而每一只蝗虫就是一个类的实例。相应的,TaskSet类
就好比是蝗虫的大脑,控制着蝗虫的具体行为,即实际业务场景测试对应的任务集。
这个比喻可能不是很准确,接下来,我将分别对Locust
和TaskSet
两个类进行详细介绍。
class HttpLocust(Locust)
在Locust类
中,具有一个client
属性,它对应着虚拟用户作为客户端所具备的请求能力,也就是我们常说的请求方法。通常情况下,我们不会直接使用Locust
类,因为其client
属性没有绑定任何方法。因此在使用Locust
时,需要先继承Locust类
,然后在继承子类中的client
属性中绑定客户端的实现类。
对于常见的HTTP(S)
协议,Locust
已经实现了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
属性并与我们实现的客户端进行绑定。后续,我们就可以像使用HttpLocust类
一样,测试其它协议类型的系统。
原理就是这样简单!
在Locust类
中,除了client
属性,还有几个属性需要关注下:
task_set
: 指向一个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
步骤,直至测试任务终止。
class TaskSet
再说下TaskSet类
。
性能测试工具要模拟用户的业务操作,就需要通过脚本模拟用户的行为。在前面的比喻中说到,TaskSet类
好比蝗虫的大脑,控制着蝗虫的具体行为。
具体地,TaskSet类
实现了虚拟用户所执行任务的调度算法,包括规划任务执行顺序(schedule_task
)、挑选下一个任务(execute_next_task
)、执行任务(execute_task
)、休眠等待(wait
)、中断控制(interrupt
)等等。在此基础上,我们就可以在TaskSet
子类中采用非常简洁的方式来描述虚拟用户的业务测试场景,对虚拟用户的所有行为(任务)进行组织和描述,并可以对不同任务的权重进行配置。
在TaskSet
子类中定义任务信息时,可以采取两种方式,@task装饰器
和tasks属性
。
采用@task装饰器
定义任务信息时,描述形式如下:
from locust import TaskSet, taskclass UserBehavior(TaskSet):@task(1)def test_job1(self):self.client.get('/job1')@task(2)def test_job2(self):self.client.get('/job2')
采用tasks属性
定义任务信息时,描述形式如下:
from locust import TaskSetdef test_job1(obj):obj.client.get('/job1')def test_job2(obj):obj.client.get('/job2')class UserBehavior(TaskSet):tasks = {test_job1:1, test_job2:2}# tasks = [(test_job1,1), (test_job1,2)] # 两种方式等价
在如上两种定义任务信息的方式中,均设置了权重属性,即执行test_job2
的频率是test_job1
的两倍。
若不指定执行任务的权重,则相当于比例为1:1
。
from locust import TaskSet, taskclass UserBehavior(TaskSet):@taskdef test_job1(self):self.client.get('/job1')@taskdef test_job2(self):self.client.get('/job2')
from locust import TaskSetdef test_job1(obj):obj.client.get('/job1')def test_job2(obj):obj.client.get('/job2')class UserBehavior(TaskSet):tasks = [test_job1, test_job2]# tasks = {test_job1:1, test_job2:1} # 两种方式等价
在TaskSet
子类中除了定义任务信息,还有一个是经常用到的,那就是on_start
函数。这个和LoadRunner
中的vuser_init
功能相同,在正式执行测试前执行一次,主要用于完成一些初始化的工作。例如,当测试某个搜索功能,而该搜索功能又要求必须为登录态的时候,就可以先在on_start
中进行登录操作;前面也提到,HttpLocust
使用到了requests.Session
,因此后续所有任务执行过程中就都具有登录态了。
脚本增强
掌握了HttpLocust
和TaskSet
,我们就基本具备了编写测试脚本的能力。此时再回过头来看前面的案例,相信大家都能很好的理解了。
然而,当面对较复杂的测试场景,可能有的同学还是会感觉无从下手;例如,很多时候脚本需要做关联或参数化处理,这些在LoadRunner
中集成的功能,换到Locust
中就不知道怎么实现了。可能也是这方面的原因,造成很多测试人员都感觉难以将Locust应用到实际的性能测试工作当中。
其实这也跟Locust
的目标定位有关,Locust
的定位就是small and very hackable
。但是小巧并不意味着功能弱,我们完全可以通过Python脚本本身来实现各种各样的功能,如果大家有疑问,我们不妨逐项分解来看。
在LoadRunner
这款功能全面应用广泛的商业性能测试工具中,脚本增强无非就涉及到四个方面:
关联
参数化
检查点
集合点
先说关联这一项。在某些请求中,需要携带之前从Server端返回的参数,因此在构造请求时需要先从之前请求的Response中提取出所需的参数,常见场景就是session_id
。针对这种情况,LoadRunner
虽然可能通过录制脚本进行自动关联,但是效果并不理想,在实际测试过程中也基本都是靠测试人员手动的来进行关联处理。
在LoadRunner
中手动进行关联处理时,主要是通过使用注册型函数,例如web_reg_save_param
,对前一个请求的响应结果进行解析,根据左右边界或其它特征定位到参数值并将其保存到参数变量,然后在后续请求中使用该参数。采用同样的思想,我们在Locust
脚本中也完全可以实现同样的功能,毕竟只是Python脚本,通过官方库函数re.search
就能实现所有需求。甚至针对html页面,我们也可以采用lxml
库,通过etree.HTML(html).xpath
来更优雅地实现元素定位。
然后再来看参数化这一项。这一项极其普遍,主要是用在测试数据方面。但通过归纳,发现其实也可以概括为三种类型。
循环取数据,数据可重复使用:e.g. 模拟3用户并发请求网页,总共有100个URL地址,每个虚拟用户都会依次循环加载这100个URL地址;
保证并发测试数据唯一性,不循环取数据:e.g. 模拟3用户并发注册账号,总共有90个账号,要求注册账号不重复,注册完毕后结束测试;
保证并发测试数据唯一性,循环取数据:模拟3用户并发登录账号,总共有90个账号,要求并发登录账号不相同,但数据可循环使用。
通过以上归纳,可以确信地说,以上三种类型基本上可以覆盖我们日常性能测试工作中的所有参数化场景。
在LoadRunner
中是有一个集成的参数化模块,可以直接配置参数化策略。那在Locust
要怎样实现该需求呢?
答案依旧很简单,使用Python的list
和queue
数据结构即可!具体做法是,在WebsiteUser
定义一个数据集,然后所有虚拟用户在WebsiteTasks
中就可以共享该数据集了。如果不要求数据唯一性,数据集选择list
数据结构,从头到尾循环遍历即可;如果要求数据唯一性,数据集选择queue
数据结构,取数据时进行queue.get()
操作即可,并且这也不会循环取数据;至于涉及到需要循环取数据的情况,那也简单,每次取完数据后再将数据插入到队尾即可,queue.put_nowait(data)
。
最后再说下检查点。该功能在LoadRunner
中通常是使用web_reg_find
这类注册函数进行检查的。在Locust
脚本中,处理就更方便了,只需要对响应的内容关键字进行assert xxx in response
操作即可。
针对如上各种脚本增强的场景,我也通过代码示例分别进行了演示。但考虑到文章中插入太多代码会影响到阅读,因此将代码示例部分剥离了出来,如有需要请点击查看《深入浅出开源性能测试工具Locust(脚本增强)》。
转载于:https://blog.51cto.com/chinalx1/2135254
相关文章:

Fedora 19下Guacamole的安装使用
由于我要使用RDP实现web远程桌面,因此需要用到了Guacamole这个开源的软件。之前用Ubuntu12.04折腾了一晚上,也没有找到依赖库文件,而Guacamole的官方安装说明却没有介绍这个依赖库如何安装,而是在RDP的配置说明里才一句话简述了这…

创建ASP.NET WEB自定义控件——例程2
本文通过一段完整的代码向读者介绍复合自定义控件的制作,包括:自定义属性、事件处理、控件间数据传递等方面的技术。 作者在http://damao.0538.org有一些控件和代码,并在更新中,有兴趣的读者可以去下载。 以下是一个登陆框的代码&…

Oracle可变数组实例说明
创建类型comm_info CREATE TYPE comm_info AS OBJECT ( /*此类型为通讯方式的集合*/ no number(3), /*通讯类型号*/ comm_type varchar2(20), /*通讯类型*/ comm_no varchar2(30)); /*号码*/ 创建可变数组comm_info_list CREATE TYPE comm_info_list AS VARRAY(50) OF com…

lua创建文件和文件夹
创建文件夹: os.execute(mkdir xx) 创建文件: f assert(io.open(a.tmp,w)) f:write(test) f:close() 转载于:https://www.cnblogs.com/cyberwalker/p/3599199.html

从定制软件到通用软件的转变
最近做了个项目,在不到一周的时间内完成一个大型网站的外壳,这是个很令人振奋的消息~!我却走了许多弯路,本来公司有自己的信息平台,从信息平台衍生出来的成型的系统也有四五个其实都是工具的拼装,而我做的部…

OPENVAS运行
https://www.jianshu.com/p/382546aaaab5

白盒测试的5种逻辑覆盖法
文章目录判定覆盖法 Decision Coverage (DC)条件覆盖 Condition Coverage (CC)判定-条件覆盖 Condition-Decision Coverage条件组合覆盖 Multiple Condition Coverage (MCC)修正的条件/判定覆盖 Modified Condition/Decision Coverage (MC/DC)5种覆盖的关系判定覆盖法 Decision…

[sinatra] Just Do It: Learn Sinatra, Part One Darren Jones
1. Install sinatra gem gem install sinatra --no-ri --no-rdoc2. Basic App #!/usr/bin/ruby require sinatra get / do"Just Do It" endruby低于1.9,需要在文件开头加require rubygems ruby basic.rbOpen up your browser and go to http://localhost:4567. 3. I…
GMTC 大前端时代前端监控的最佳实践
摘要: 今天我分享的内容分成三个部分: 第一部分是“大前端时代前端监控新的变化”, 讲述这些年来,前端监控一些新的视角以及最前沿的一些思考。 第二部分"前端监控的最佳实践", 从使用的角度出发,介绍前端监…

Visual C#访问接口
对接口成员的访问 对接口方法的调用和采用索引指示器访问的规则与类中的情况也是相同的。如果底层成员的命名与继承而来的高层成员一致,那么底层成员将覆盖同名的高层成员。但由于接口支持多继承,在多继承中,如果两个父接口含有同名的成员&am…

powerdesigner类图在子类中显示从父类继承来的方法
首先确保画了子类和父类之间的继承线 然后在子类的选项卡中点击

[UML]UML系列——用例图中的各种关系(include、extend)
[UML]UML系列——用例图中的各种关系(include、extend) 原文:[UML]UML系列——用例图中的各种关系(include、extend)用例图中的各种关系 一、参与者与用例间的关联关系 参与者与用例之间的通信,也成为关联或通信关系。…

熬夜写了一个小游戏,向SpaceX聊表敬意
2019独角兽企业重金招聘Python工程师标准>>> 这是我长久放在桌面上的一张图片。 这张照片的名字叫做 Pale Blue Dot(暗淡蓝点),是旅行者1号在距地球64亿公里回望太阳系时所拍下的。照片右侧中部有一个隐约可见的小蓝点,…

【转】Linux Oracle服务启动停止脚本与开机自启动
在CentOS 6.3下安装完Oracle 10g R2,重开机之后,你会发现Oracle没有自行启动,这是正常的,因为在Linux下安装Oracle的确不会自行启动,必须要自行设置相关参数,首先先介绍一般而言如何启动oracle。 一、在Lin…

ASP.Net中MD5加密-16位32位
publicstringmd5(stringstr,intcode){if(code16) //16位MD5加密(取32位加密的9~25字符){return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str,"MD5").ToLower().Substring(8,16) ;} else//32位加密{retur…

PL/SQL编程:将两个数进行交换的存储过程
创建过程exchangeTwo 调用过程enchangeTwo

WCF之多个终结点
1.服务端配置如下(一个Service节点下可有多个endpoint,): 1 1<system.serviceModel>2 2 <services>3 3 <service name"Microsoft.ServiceModel.Samples.CalculatorService" behaviorConfiguration"returnFaul…

YAML 语言入门教程
2019独角兽企业重金招聘Python工程师标准>>> YAML简介 另一种标记语言(Yet Another Markup Language) YAML(英语发音:/ˈjməl/,尾音类似camel骆驼)是一个可读性高,用来表达资料序列…

我翻译的一篇文章,OO设计中对象的创建和使用
OO设计中对象的创建和使用 转载于:https://www.cnblogs.com/lishu1980/archive/2006/06/22/432526.html

SQL Server Profiler工具
SQL Server Profiler工具 原文:SQL Server Profiler工具一、SQL Profiler工具简介 SQL Profiler是一个图形界面和一组系统存储过程,其作用如下: 图形化监视SQL Server查询;在后台收集查询信息;分析性能;诊断像死锁之类…

Nginx—核心配置location匹配规则说明
2019独角兽企业重金招聘Python工程师标准>>> location介绍 location指令是Nginx中最核心的一项配置,根据预先定义的URL匹配规则来接收用户发送的请求,根据匹配结果,将请求转发到后台服务器、非法的请求直接拒绝并返回403ÿ…

Windows Live Messenger 正式版已经发布
Windows Live Messenger 正式版已经发布!这是新一代的 Messenger。它拥有全新的名字,可以免费下载。并且除了延续使用原 Messenger 的全部功能外,它还提供了几种出色的全新交流方式,实现与朋友的即时共享和交流。转载于:https://www.cnblogs.…

关闭ubuntu启动时System Program Problem Detected提示
修改vi /etc/default/apport 将其值设置1-->0转载于:https://www.cnblogs.com/snail-micheal/p/3607969.html

C++开源跨平台类库集
在如下的库支持下,开发的系统可以很方便移植到当前大部分平台上运行而无需改动,只需在对应的平台下 用你喜欢的编译器 重新编译即可 经典的C库 STLport-------SGI STL库的跨平台可移植版本,在以前有些编译器离符合 标准比较远的情况…

僵尸进程和孤儿进程
一 . 僵尸进程: 子进程退出,父进程没有回收子进程资源(PCB),则子进程变成僵尸进程 处理僵尸进程的方法:wait 和 waitpid 调用wait和waitpid不仅可以获得子进程的终止信息,还可以使父进程阻塞等待子进程终止…

Castle ActiveRecord(一)概述
一、ActiveRecord与Castle ActiveRecordActiveRecord是《Patterns of Enterprise Application Architecture》中描述的著名模式。基本上,当实例每一行的时候,所有的静态方法会作用于全部纪录。 Castle ActiveRecord 是ActiveRecord 模式的一个实现&am…