• 0
  • 0
分享
  • 性能测试工具-Locust的使用方法——软件测试圈
  • 曼倩诙谐 2023-12-05 11:10:53 字数 8806 阅读 1219 收藏 0

  Locust介绍

  Locust是一款使用Python开发的开源性能测试工具,支持分布式,可在多台主机上对系统持续发送请求,与Jmeter、LoadRunner的等压测工具使用线程的方式模拟用户请求不同,Locust是使用协程的方式模拟用户请求,协程的上下文切换是由自己控制,当一个协程执行完成后会主动让出,让另一个协程开始执行,切换是在用户态完成的,而线程切换是受系统控制,是在用户态与内核态之间切换,所以协程上下文切换的代价远比线程切换的代价小的多,因此Locust可以达到更高数量级的并发。

  Locust安装

  需要先安装python3.6以上版本,然后再安装Locust。

  pip install locust # 安装locust
  locust -V # 查看版本

  Locust文件编写

  locust是通过编写python脚本进行测试的,直接看代码吧!对于单接口场景,如下所示:

  TaskSet类和SequentialTaskSet类实现了虚拟用户所执行任务的调度算法,包括挑选任务、执行任务、规划执行顺序等等;HttpUser中有一个用于发送http请求的client属性,在发送请求前需先调用HttpUser类;task装饰器是为了给用户添加任务,只有带有task装饰器的方法才会发起请求;constant是设置固定等待时间。

  # OPLogin.py文件
  import re
  from locust import HttpUser,task,TaskSet,constant
  # openstack登录
  class MyTask(TaskSet):
  # on_start/on_stop是前置和后置操作方法,与jmeter中的setup/teardown类似,开始前后执行一次
  def on_start(self):
  self.op_url = "/dashboard/auth/login/"
  # 访问登录页面
  res = self.client.get(url=self.op_url, name="获取token")
  # 使用正则表达式在响应文档中提取动态token
  csrf = re.findall('name="csrfmiddlewaretoken" value="(.+(?=">))', res.text)
  # 将列表转为字符串,使用join方法将非字符串的格式内容都去掉,把列表中的内容都取出来,用空字符进行连接
  self.token = "".join(csrf)
  self.cookie = res.cookies
  print("********** 开始执行性能测试 **********")
  def on_stop(self):
  print("********** 测试结束 **********")
  @task # @task装饰器的作用是将opLogin标记为可被调用的方法
  def opLogin(self):
  # 登录参数
  data = {
  "csrfmiddlewaretoken": self.token, # 调用on_start中获取的token
  "region": "default",
  "username": "admin",
  "password": "0565317d22c54a97"
  }
  # 发送登录请求,url与on_start中的一样,直接调用
  resp = self.client.request(method="post",url=self.op_url,data=data,name="登录openstack")
  try: # 断言,判断响应文本中是否包含指定内容,包含则返回“成功”,否则返回“失败”并打印异常
  assert "Instance Overview - OpenStack Dashboard" in resp.text
  print("登录成功")
  except Exception as e:
  print("登录失败,未找到【Instance Overview - OpenStack Dashboard】",e)
  class OPlogin(HttpUser):
  tasks = [MyTask] # 要执行的任务
  host = "http://192.166.66.33" # 被测网站域名
  wait_time = constant(3) # 请求间隔时间,每次请求间隔固定3s

  对于多接口测试,也是同样的操作,在每个方法上加上task装饰器即可,task装饰器上可添加权重参数,如下所示:

  # CreateRES.py文件
  import re
  from locust import HttpUser,task,TaskSet
  class MyTask(TaskSet):
  # 由于未登录无法访问创建资源,所以先直接将获取token和登录都放到on_start中
  def on_start(self):
  self.op_url = "/dashboard/auth/login/"
  # 发送获取token请求
  res = self.client.get(url=self.op_url, name="获取token")
  csrf = re.findall('name="csrfmiddlewaretoken" value="(.+(?=">))', res.text)
  self.token = "".join(csrf)
  self.cookie = res.cookies
  data = {
  "csrfmiddlewaretoken": self.token,
  "region": "default",
  "username": "admin",
  "password": "0565317d22c54a97"
  }
  # 发送登录请求
  self.client.request(method="post",url=self.op_url,data=data,name="登录openstack")
  print("********** 开始执行性能测试 **********")
  @task(6) # (6)是可选权重参数,自定义权重占比为6
  def CreateVolume(self):
  vol_url = "/dashboard/project/volumes/create/"
  vol_data = {
  "csrfmiddlewaretoken":self.token, # 调用on_start中获取的token
  "name":"dyd-volume",
  "volume_source_type":"no_source_type",
  "type":"iscsi",
  "size":"1",
  "availability_zone":"nova"
  }
  # 发送创建云硬盘的请求,需有cookie才能创建成功,在on_start中获取cookie后调用
  self.client.request(method="post",url=vol_url,data=vol_data,cookies=self.cookie,name="创建云硬盘")
  @task(3) # 权重占比为3
  def CreateSecGroup(self):
  csecg_url = "/dashboard/project/security_groups/create/"
  csecg_data = {"csrfmiddlewaretoken":self.token,"name":"dyd-SecG"}
  self.client.request(method="post",url=csecg_url,data=csecg_data,name="创建安全组")
  @task
  def SecGList(self):
  secg_url = "/dashboard/project/security_groups/"
  self.client.request(method="get",url=secg_url,name="查看安全组列表")
  class OPlogin(HttpUser):
  tasks = [MyTask]
  host = "http://192.166.66.33"

  对于业务流的测试,需要使用SequentialTaskSet类,可以按代码顺序由上到下执行带有task装饰器的任务。

  # Operflow.py文件
  import re
  from locust import HttpUser,task,between,SequentialTaskSet
  # openstack中创建云硬盘业务流性能测试
  class MyTask(SequentialTaskSet):
  @task # 步骤一,先获取token
  def get_token(self):
  self.op_url = "/dashboard/auth/login/"
  res = self.client.get(url=self.op_url, name="获取token")
  # 使用正则表达式在响应文档中提取动态token
  csrf = re.findall('name="csrfmiddlewaretoken" value="(.+(?=">))', res.text)
  # 将列表转为字符串,使用join方法将非字符串的格式内容都去掉,把列表中的内容都取出来,用空字符进行连接
  self.token = "".join(csrf)
  self.cookie = res.cookies
  print("********** 开始执行性能测试 **********")
  @task # 步骤二,拿到token后登录到平台
  def opLogin(self):
  data = {
  "csrfmiddlewaretoken": self.token, # 调用on_start中获取的token
  "region": "default",
  "username": "admin",
  "password": "0565317d22c54a97"
  }
  # 发送登录请求,url与on_start中的一样,直接调用
  resp = self.client.request(method="post",url=self.op_url,data=data,name="登录openstack")
  try: # 断言,判断响应文本中是否包含指定内容,包含则返回“成功”,否则返回“失败”并打印异常
  assert "Instance Overview - OpenStack Dashboard" in resp.text
  print("登录成功")
  except Exception as e:
  print("登录失败,未找到【Instance Overview - OpenStack Dashboard】",e)
  @task # 步骤三,登录平台后创建资源
  def CreateVolume(self):
  vol_url = "/dashboard/project/volumes/create/"
  vol_data = {
  "csrfmiddlewaretoken":self.token,
  "name":"dyd-volume",
  "volume_source_type":"no_source_type",
  "type":"iscsi",
  "size":"1",
  "availability_zone":"nova"
  }
  # 发送创建云硬盘的请求
  self.client.request(method="post",url=vol_url,data=vol_data,cookies=self.cookie,name="创建云硬盘")
  class OPlogin(HttpUser):
  tasks = [MyTask]
  host = "http://192.166.66.33"
  wait_time = between(0.5,2) # 请求间隔时间,会在0.5~2s之间产生一个随机数作为思考时间

  也可以只调用HttpUser类,通常代码量少时使用,tag装饰器是为了标记任务,可以执行指定带有某个标记的任务。

  # jpress.py文件
  from locust import HttpUser, task, tag
  class MyJPress(HttpUser):
  host = "http://192.166.66.22:8080"
  @tag("login")
  @task
  def dologin(self):
  login_url = "/user/doLogin"
  data = {"user":"admin","pwd":"123456"}
  self.client.request(method="post",data=data,url=login_url,name="登录JPress")
  @tag("details","info")
  @task
  def lkarticle(self):
  url = "/article/2"
  self.client.request(method="get",url=url,name="查看文章详情")

  断言

  上文中登录通过查找响应结果中是否包含关键字判断是否正确,还可以通过响应码、响应状态、响应时间等方法进行断言。

  from locust import HttpUser, task, tag
  class MyJPress(HttpUser):
  host = "http://192.166.66.22:8080"
  @task
  def dologin(self):
  login_url = "/user/doLogin"
  data = {"user":"admin","pwd":"123456"}
  resp = self.client.request(method="post",data=data,url=login_url,name="登录JPress")
  if resp.status_code != 200: # 通过状态码判断
  print("访问出错啦!暂时无法登录")
  elif resp.json()["state"] == "ok": # 通过响应状态判断
  print("登录成功")
  elif resp.elapsed.total_seconds() > 1: # 通过响应时长判断
  print("请求响应时间过长")
  @task
  def lkarticle(self):
  url = "/article/2"
  # 使用catch_response参数和with语句判断请求结果
  with self.client.request(method="get",url=url,name="查看文章详情",catch_response=True) as res:
  if "小测试" in res.text:
  res.success()
  else:
  res.failure("无法查看文章详情!")

  参数化

  可借助第三方库mimesis或faker进行参数化,可自动生成账号、密码、邮箱、文本、地址、日期等。

  pip install mimesis # 安装mimesis库
  from locust import HttpUser, task, tag
  from mimesis import Text # 导入mimesis库中的Text模块
  class MyJPress(HttpUser):
  host = "http://192.166.66.22:8080"
  @task
  def dologin(self):
  login_url = "/user/doLogin"
  data = {"user":"admin","pwd":"123456"}
  resp = self.client.request(method="post",data=data,url=login_url,name="登录JPress")
  @task
  def writeArt(self):
  csrf_url = "/ucenter"
  csrf = self.client.request(method="get", url=csrf_url, name="获取csrf_token")
  self.token = csrf.cookies["csrf_token"] # 提取csrf_token值
  war_url = "/ucenter/article/doWriteSave"
  art = Text("zh")
  data = {"article.edit_mode":"markdown",
  "article.title": art.color() + art.level() +"……", # 使用mimesis生成标题
  "article.content":art.text(), # 使用mimesis生成文章内容
  "csrf_token": self.token
  }
  resp = self.client.request(method="post",url=war_url,data=data,name="投稿文章")

  Locust文件执行

  GUI模式

  单击压测,-f表示要执行的文件,--tags表示执行带有指定标签的任务。

  locust -f 路径/文件名.py # 执行文件中所有待@task装饰器的任务
  locust -f 路径/文件名.py --tags login info # 执行文件中标签为login、info的任务

  之后在浏览器中输入IP:8089访问Web页面,输入总用户数和每秒增加用户数,因为代码中已经定义Host,Web页面会自动带入,若未定义可在命令中带上--host参数,或者直接在Web页面输入。

1-1.png

  分布式压测,使用多台主机时需指明主从机器,--master表示作为主机使用,--worker表示作为从机使用,--master-host与--worker一起使用,设置主机的主机名或IP,分布式压测每台机器上都要需要安装locust,而且需要将文件拷贝至所用到的每台机器上。

  locust -f 路径/文件名.py --master # 在主机上执行此命令,Web界面会在此主机上运行
  locust -f 路径/文件名.py --worker --master-host=192.166.66.22 # 在每台从机上执行此命令,指定主机IP

1-2.png

  非GUI模式

  单机压测,--headless表示使用没有UI界面的方式运行,-u表示总用户数,-r表示每秒增加的用户数,-t表示测试运行时间,--html是输出测试报告,也可以使用--csv导出csv格式的测试结果,会导出4个结果文件,-t、--html、--csv都是可选参数。

  locust -f 路径/文件名.py --headless -u 30 -r 2 -t 1h30m --html=路径/报告名.html

1-3.png

  分布式压测,非GUI模式使用以下命令,--expect-workers表示等待几个从机连接后再开始测试,从机个数达到要求后会立即开始执行。

  locust -f 路径/文件名.py --headless -u 10 -r 2 -t 5s --master --expect-workers=2 # 在主机上执行,可带上报告参数
  locust -f 路径/文件名.py --headless --worker --master-host=192.166.66.22 # 在每台从机上执行此命令

1-4.png

  监控工具

  基本性能指标:响应时间、吞吐量、资源利用率,使用Locust自带的监控功能即可。

  也可以使用系统自带的监控工具:vmstat、top,或nmon、dstat,后两项需要使用yum命令安装。

  还可以使用Prometheus+Grafana实现更炫酷的监控图表,如下所示,整个压测过程系统性能变化会以图表形式展示。

1-5.png

  通常一个正常系统应随着压力增大CPU的利用率也应不断升高,所以性能测试首先关注的是CPU,CPU上不去就意味着压力上不去,压力上不去又意味着无法测试系统的处理能力,继续压测并没有什么意义。单台机器的CPU处理能力是有限的,所以通常采用分布式的方式进行性能测试,从上文的报告就可以看出单台机器与分布式测试的RPS是有很大差距的,只有压力上去之后测试的结果才更准确,更容易确定系统瓶颈,常见的系统瓶颈有线程阻塞、数据库加锁、磁盘IO阻塞等。


作者:小白典    

来源:http://www.51testing.com/html/86/n-5099586.html

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 前提条件:Jmeter已安装且已配置好;运行Jmeter,打开界面。第一步,添加线程组在Jemeter界面上有一个默认的测试计划根路径。一个测试计划包含一系列Jmeter运行时要执行的步骤,包含一个或者多个线程组,逻辑控制器,取样发生控制,监听器,定时器,断言和配置元件等。我们想要创建一个完整的测试计划,那么就先创建一个线程组:选中测试计划,点击鼠标右键-> 添加->线程(用户)->线程组,如下图所示:线程组元件是任何测试计划的起点。一个测试计划的所有元件必须在一个线程组下。线程组元件控制JMeter运行测试时使用的线程数。线程组管理可以:设置线程数设置ramp-up per...
            0 0 4797
            分享
          •   世界上最遥远的距离不是我说还是没说,而是我说了什么你却没明白是怎么回事。  最近小编有幸参加了一场金字塔原理的培训课程,金字塔原理帮助我们解决两大问题:思维混乱、逻辑不清,通过金字塔的学习,可以做到想清楚、说明白。下边小编通过小明的故事,跟大家分享一下金字塔原理的工作的方式。  一天,小明在电梯里遇到了部门领导,领导问:小明,好久不见,最近在忙什么?  小明瞬间脑袋空白,不知道如何回答,支支吾吾的说:还好,没忙什么,就是在做5.0版本的项目测试。  小明不开心的回到工位,看到了群群,群群帮他分忧,群群说:小明,你可以采用时间逻辑进行回答,比如我上个月在做一个创新项目升级策略的工具,这个月刚...
            0 0 2490
            分享
          •   关于越权漏洞,大家都熟知水平越权、垂直越权,未授权访问,此处不再赘述概念了。对于越权类漏洞的测试,通用的测试方法,也都是人工通过代理抓包工具截获报文,然后尝试删除Cookie测试是否存在访问越权,或者篡改Uid、Uno之类的值测试是否存在业务逻辑越权等。这种人工检测越权类漏洞的方法,不仅工作量大,而且效率低,还容易产生遗漏。AppScan作为一个自动化的渗透测试工具,具备自动扫描越权漏洞的能力,可大大提高测试覆盖率和效率,减少人工成本。所以,快跟我一起挖掘AppScan的这项隐藏技能吧。  这个功能的位置就在扫描配置-测试-特权升级,AppScan将越权叫做特权升级,表述略有不同,但是同一...
            13 14 1000
            分享
          •   两个熟悉的场景:  ·生产环境出现问题,解决问题,原因复盘、责任分配到人;  · 无休止的测试-回归-再测试-再回归测试,已经投入了很大精力,但仍对项目质量不信心;  如果自己所负责或参与的项目经常遇到上面的两种情况,不妨从项目测试流程角度,去思考原因以及破开瓶颈的方法。  测试流程拆解  需求评审  通过参与技术设计评审,可以为测试方案提供依据。例如:核心业务是否需要接口测试、新老数据兼容问题、测试场景的数据构造以及测试所需的工具等,都可以在这个阶段进行思考和产出。  另外,可以有效的评估需求影响范围和风险点,避免遗漏。  此阶段是质量的基石,通过测试左移,尽早发现需求设计缺陷...
            0 0 1111
            分享
          •   PICT(Pairwise Independent Combinatorial Testing,成对独立组合测试)是微软开发的一款测试用例生成工具(生成配对测试用例的工具很多,感兴趣的可以参考http://www.pairwise.org/tools.asp的介绍),它可以生成测试用例和测试配置,其理论基础是成对测试技术(Pairwise Testing,之前有文章单独介绍过,在此不多介绍)。  一、PICT安装  通过官网链接?http://download.microsoft.com/download/f/5/5/f55484df-8494-48fa-8dbd-8c6f76cc014b...
            13 13 2840
            分享
      • 51testing软件测试圈微信