深聊性能测试,从入门到放弃之:Locust性能自动化(二)代码实战

简介: 深聊性能测试,从入门到放弃之:Locust性能自动化(二)代码实战

1. 引言


在本章节,你可以学习到:

1、Locust代码实例展示及解读:

①官网代码示例

②demo模板代码

3、Loucst的高级用法:

①关联

②参数化

③检查点

4、Locust的运行模式:

①单进程运行模式

②多进程分布式运行

5、Locust界面展示及结果分析


2. Locust实例展示


2.1 官网代码示例

我们来看看官网的第一个例子,

很简单:

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import HttpUser, between, task
class WebsiteUser(HttpUser):
  #设置等待时间间隔
    wait_time = between(5, 15)
    def on_start(self):
        self.client.post("/login", {
            "username": "test_user",
            "password": ""
        })
    @task
    def index(self):
        self.client.get("/")
        self.client.get("/static/assets.js")
    @task
    def about(self):
        self.client.get("/about/")

这里有几点说一下:

1、between: 设置等待时间, 5s~15s;

2、client.get/ client.post: 用法跟request是一样的。

其他的就没有什么可以重点强调的!


2.2 Locust 代码模板及执行顺序


这段代码,小鱼展示两点:

1、locust demo模板;
2、locust 代码执行顺序。

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import HttpUser,TaskSet,task
'''
执行顺序:
Locust setup → TaskSet setup → TaskSet on_start → 
TaskSet tasks → TaskSet on_stop → TaskSet teardown → 
Locust teardown
'''
class UserBehavor(TaskSet):
    #启动locust是运行setup方法
    def setup(self):
        print('task setup')
    def teardown(self):
        print('task teardown')
    #虚拟用户启动task时运行
    def on_start(self):
        print('start')
    #虚拟用户结束task时运行
    def on_stop(self):
        print('end')
    @task(2)
    def index(self):
        self.client.get('/')
    @task(1)
    def profile(self):
        self.client.get('/profile')
class WebsitUser(HttpUser):
    def setup(self):
        print('locust setup')
    def teardown(self):
        print('locust teardown')
    host = 'http://xxx.com'
    task_set = task(UserBehavor)
    min_wait = 100
    max_wait = 5000
if __name__ == '__main__':
    pass

虽然小鱼展示了模板,可以直接使用,但是,里面的内容,需要各位大佬自己填充~~

毕竟 业务不同,填充的内容也不一样!!

小屌丝:鱼哥,感觉你在开车,但是没证据!!

小鱼:都快一点了,你还不睡觉,白富美不香吗!!!

小屌丝:…


3. Locust 类代码分析


3.1 实例代码展示

关于locust类的详细讲解,放在了第一章节,因为

小鱼觉得:先了解基础,再去看代码,这样就不至于看代码想看天书,至少遇到一个类,能有一个印象。

这里,为了方便大家,点击下方带颜色的文字即可进入第一章节:

《深聊性能测试,从入门到放弃之:Locust性能自动化(一)初识Locust》

回归正题,老规矩,先上代码,再逐层分析

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import HttpUser,task,TaskSet
'''
在版本10.1,已经不再使用HttpLocust 和Locust,
取而代之的是HttpUser 和User
'''
# 定义ScriptTasks类,继承TaskSet类
class ScriptTasks(TaskSet):
    #初始化,每个locust用户开始做的第一件事
    def on_start(self):
        #放置 用户名和密码
        self.client.post('/login', {
        "username":"carl_dj",
        "password":'111111'
        })
    #@task()装饰的方法为一个事务,方法的参数用于指定该行为的执行权重,参数越大每次被虚拟用户执行的概率越高,默认为1
    @task(2)
    #创建index方法,
    def index(self):
        self.client.get('/')
    @task(1)
    def about(self):
        #self.client 属性使用python的request库的方法,调用和使用方法和request一样
        self.client.get('/about')
    @task(2)
    def demo(self):
        payload = {}
        headers = {}
        self.client.post('/demo', data = payload,headers = headers)
#TaskSet类,该类定义用户任务信息(模拟用户信息),
class WebsitUser(HttpUser):
    #指向一个定义的用户行为
    task_set = task(ScriptTasks)
    #被测系统的host,
    host = 'http://www.xxxxxx.com'
    #每个用户执行两个任务间隔时间最小值,单位是(毫秒,默认是1000ms)
    min_wait = 100
    # 每个用户执行两个任务间隔时间最大值,单位是(毫秒)
    max_wait = 5000

这里小鱼在强调一次:

1、关于 HttpUser 和User的使用, 在版本10.1之后,就需要换成HttpUser 和 User,否则报错;

>>>因为小鱼发现,很多网站的大佬都在使用HttpLocust 和Locust,如果你的Locust 版本是 9.x或者8.x,可以使用,不做强要求。


3.2 classTa

skSet 用法及展示

3.2.1 定义

1、TaskSet类实现了虚拟用户所执行任务的调度算法,包括:

①规划任务执行顺序:schedule_task;

②挑选下一个任务:execute_next_task;

③执行任务:execute_task;

④休眠等待:wait;

⑤中断控制:interrupt;


2、在1的基础上,就可以在TaskSet子类中进行以下操作:

①描述虚拟用户的业务测试场景;

②对虚拟用户的所有行为进行组织和描述;

③对不同任务的权重进行配置;


3、 @task

①通过@task()装饰的方法为一个事务。

>>>参数越大每次被虚拟用户执行的概率越高,默认是1。


4、TaskSet子类中采用2种方式定义任务信息:

① @task

② tasks属性


3.2.2 代码展示

1、采用@task装饰器定义任务息:

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import task,TaskSet
class UserBehav(TaskSet):
    @task(2)
    def test_case1(self):
        self.client.get("/testcase1")
    @task(4)
    def test_case2(self):
        self.client.get("/testcase2")

2、采用tasks属性定义任务信息:

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import TaskSet
def test_case1(self):
    self.client.get("/testcase1")
def test_case2(self):
    self.client.get("/testcase2")
class UserBehav(TaskSet):
    tasks = {test_case1:2,test_case2:4}
    #另一种写法
    # tasks = [(test_job1,1), (test_job1,3)]

上面的代码,没有什么难度,这里就不做解释。


4. Locust高级用法


4.1 关联

做过接口或者爬虫的的大佬都知道,传参是必不可少的,

而常见的场景有session_id。

对于返回的html页面,可用采用lxml库来定位获取需要的参数。

我们先上代码

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import HttpUser,task,TaskSet
from lxml import etree
class WebsitTasks(TaskSet):
    #获取session
    def get_session(self,html):
        tags = etree.HTML(html)
        return tags.xpath("输入标签需要定位的到元素")
    #启动
    def on_start(self):
        html = self.client.get('/index')
        session = self.get_session(html.text)
        #设置payload参数
        payload = {
            'username': 'carl_dj',
            'password':'111111',
            'session':session
        }
        #设置header参数
        header = {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36"}
        self.client.post('/login',data = payload,headers = header)
    @task(5)
    def index(self):
        self.client.get('/')
    @task(1)
    def  about(self):
        self.client.about('/about/')
class WebsiteUser(HttpUser):
    # 被测系统的host,在终端中启动locust时没有指定--host参数时才会用到
    host     = "http://www.xxx.com/user/login"
    # TaskSet类,该类定义用户任务信息,必填。这里就是:WebsiteTasks类名,因为该类继承TaskSet;
    task_set = task(WebsiteTasks)
    # 每个用户执行两个任务间隔时间的上下限(毫秒),具体数值在上下限中随机取值,若不指定默认间隔时间固定为1秒
    min_wait = 5000
    max_wait = 15000

嗯,详细的内容都在代码中标注,这里就不再重新唠叨。


4.2 参数化


4.2.1 聊一聊参数化

老话说的好:代码写死一时爽,框架重构火葬场

虽然大部分大佬还没有涉及到 设计框架的阶段,但是,只要稍微努努力…

火葬场 迟早都是要去滴~ ~

所以,就有了另一句老话:动态代码一时爽,一直动态一时爽

可见,参数化的作用,真的,很Nice!

话说回来,参数化的作用是啥呢:循环取数据,数据可重复使用。


4.2.2 三个场景认识参数化

场景1:

>> 模拟3个用户并发请求网页,共有100个URL地址,每个虚拟用户都会依次循环加载100个URL地址

代码展示:

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import TaskSet, task, HttpUser
class UserBehav(TaskSet):
    def on_start(self):
        self.index = 0
    @task
    def test_visit(self):
        url = self.locust.share_data[self.index]
        print('visit url: %s' % url)
        self.index = (self.index + 1) % len(self.locust.share_data)
        self.client.get(url)
class WebsiteUser( HttpUser):
    host = 'http://www.xxx.com'
    task_set = task(UserBehav)
    share_data = ['url1', 'url2', 'url3', 'url4', 'url5']
    min_wait = 1000
    max_wait = 15000

场景2

>>>模拟3用户并发注册账号,共有9个账号,要求注册账号不重复,注册完毕后结束测试

概括:

保证并发测试数据唯一性,不循环取数据

>>>所有并发虚拟用户共享同一份测试数据,并且保证虚拟用户使用的数据不重复;

代码

采用队列

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import TaskSet, task, HttpUser
import queue
class UserBehav(TaskSet):
    @task
    def test_register(self):
        try:
            data = self.locust.user_data_queue.get()
        except queue.Empty:
            print('account data run out, test ended.')
            exit(0)
        print('register with user: {}, pwd: {}'\
            .format(data['username'], data['password']))
        payload = {
            'username': data['username'],
            'password': data['password']
        }
        self.client.post('/register', data=payload)
class WebsiteUser(HttpUser):
    host = 'http://www.xxx.com'
    task_set = task(UserBehav) 
    user_data_queue = queue.Queue()
    for index in range(100):
        data = {
            "username": "test%04d" % index,
            "password": "pwd%04d" % index,
            "email": "test%04d@debugtalk.test" % index,
            "phone": "186%08d" % index,
        }
        user_data_queue.put_nowait(data)
    min_wait = 1000
    max_wait = 15000

场景3

>>>模拟3个用户并发登录账号,总共有9个账号,要求并发登录账号不相同,但数据可循环使用;

概括:

保证并发测试数据唯一性,循环取数据;

>>>所有并发虚拟用户共享同一份测试数据,保证并发虚拟用户使用的数据不重复,并且数据可循环重复使用。

代码展示

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import TaskSet, task, HttpUser
import queue
class UserBehav(TaskSet):
    @task
    def test_register(self):
        try:
            data = self.locust.user_data_queue.get()
        except queue.Empty:
            print('account data run out, test ended')
            exit(0)
        print('register with user: {0}, pwd: {1}' .format(data['username'], data['password']))
        payload = {
            'username': data['username'],
            'password': data['password']
        }
        self.client.post('/register', data=payload)
        self.locust.user_data_queue.put_nowait(data)
class WebsiteUser(HttpUser):
    host = 'http://www.xxx.com'
    task_set = task(UserBehav)
    user_data_queue = queue.Queue()
    for index in range(100):
        data = {
            "username": "test%04d" % index,
            "password": "pwd%04d" % index,
            "email": "test%04d@debugtalk.test" % index,
            "phone": "186%08d" % index,
        }
        user_data_queue.put_nowait(data)
    min_wait = 1000
    max_wait = 15000


4.3 检查点

我们直接使用assert来进行断言操作。

上代码:

# -*- coding: utf-8 -*-
"""
@ auth : carl_DJ
@ time : 2020-9-23
"""
from locust import task
@task
def test_interface(self):
     #直接使用csdn的某一个api
    with  self.client.get("https://editor.csdn.net/md?articleId=108596407",name = 'fileconfig',catch_response=True) as response:
      #python断言对接口返回值中的max字段进行断言
        assert response.json()['rating']['max']==100           
        #对http响应码是否200进行判断
        if response.status_code ==200:                   
            response.success()
        else:
            response.failure("Failed!")

这里说明一下:

1、断言形式:with self.client.get(“url地址”,catch_response=True) as response;


2、response.status_code获取http响应码进行判断,失败后会加到统计错误表中;

>>>如果直接使用python自带assert,则不会进入到locust报表,


3、默认不写参数catch_response=False断言无效,将catch_response=True才生效。


5. Locust运行模式

运行Locust时,通常会使用到两种运行模式:单进程运行和多进程分布式运行。


5.1 单进程运行模式

5.1.1 定义及解析

1、Locust所有的虚拟并发用户均运行在单个Python进程中,

由于单进程的原因,并不能完全发挥压力机所有处理器的能力,因此主要用于调试脚本和小并发压测的情况。


2、当并发压力要求较高时,就需要用到Locust的多进程分布式运行模式。

>>> 一旦单台计算机不足以模拟所需的用户数量,Locust就会支持运行分布在多台计算机上的负载测试。


3、多进程分布运行情况:

①多台压力机同时运行,每台压力机分担负载一部分的压力生成;

②同一台压力机上开启多个slave的情况。

>>>如果一台压力机有N个处理器内核,那么就在这台压力机上启动一个master,N个slave。

>>>也可以启动N的倍数个slave。


5.1.2 有Web UI模式

Locust默认采用8089端口启动web;如果要使用其它端口,就可以使用如下参数进行指定。


参数说明:


① -P, --port:指定web端口,默认为8089.

终端中—>进入到代码目录: locust -f locustfile.py --host = xxxxx.com

② -f: 指定性能测试脚本文件

③ -host: 被测试应用的URL地址【如果不填写,读取继承(HttpLocust)类中定义的host】


注意

1、如果Locust运行在本机,在浏览器中访问http://localhost:8089即可进入Locust的Web管理页面;


2、如果Locust运行在其它机器上,那么在浏览器中访问http://locust_machine_ip:8089即可。


5.1.3 无Web UI模式

如果采用no_web形式,则需使用–no-web参数,并会用到如下几个参数。


参数说明:

① -c, --clients:指定并发用户数;

② -n, --num-request:指定总执行测试次数;

③ -r, --hatch-rate:指定并发加压速率,默认值位1。

示例展示:

$ locust -f    locustfile.py     --host = xxxxx   --no-web -c 1 -n 2


5.1.4 启动locust

在Pycharm的 的Terminal 中启动 locust,

输入内容:

locust  --host =http://localhost  -f test_load.py


5.2 多进程分布式运行

不管是单机多进程,还是多机负载模式,运行方式都是一样的,都是先运行一个master,再启动多个slave。

5.2.1 master启动

1、启动master时,需要使用–master参数

2、如果要使用8089以外的端口,还需要使用-P, --port参数

示例展示:

locust -f prof_load.py --master --port=8089

5.2.2 slave 启动

1、启动slave时需要使用–slave参数

2、在slave中,就不需要再指定端口

3、master启动后,还需要启动slave才能执行测试任务

示例展示

locust -f monitorAgent.py --slave
ocust -f monitorAgent.py --slave --master-host=<locust_machine_ip>


6. Locust 结果分析


Number of users to simulate: 设置虚拟用户数,对应中no_web模式的-c, --clients参数;

Hatch rate(users spawned/second): 每秒产生(启动)的虚拟用户数 , 对应着no_web模式的-r, --hatch-rate参数,默认为1。

上图:启动了一个 master 和两个 slave,由两个 slave 来向被测试系统发送请求

性能测试参数

Type: 请求的类型,例如GET/POST。

Name:请求的路径。这里为百度首页,即:https://www.baidu.com/

request:当前请求的数量。

fails:当前请求失败的数量。

Median:中间值,单位毫秒,一半的服务器响应时间低于该值,而另一半高于该值。

Average:平均值,单位毫秒,所有请求的平均响应时间。

Min:请求的最小服务器响应时间,单位毫秒。

Max:请求的最大服务器响应时间,单位毫秒。

Content Size:单个请求的大小,单位字节。

reqs/sec:是每秒钟请求的个数。

相比于LoadRunner,Locust的结果展示十分简单,主要就四个指标:并发数、RPS、响应时间、异常率。但对于大多数场景来说,这几个指标已经足够了。

上图是 曲线分析图。

关于locust的代码实战及结果分析,就先到这里。

在这里,小鱼再多说一句:

万行代码从头写,先看基础挺要紧

所以,要弄懂代码,还是先看基础。

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
目录
相关文章
|
1月前
|
Java 关系型数据库 数据库连接
Mybatis+MySQL动态分页查询数据经典案例(含代码以及测试)
Mybatis+MySQL动态分页查询数据经典案例(含代码以及测试)
28 1
|
1月前
|
测试技术
现代软件测试中的自动化工具与挑战
传统软件测试面临着越来越复杂的系统架构和不断增长的测试需求,自动化测试工具应运而生。本文将探讨现代软件测试中自动化工具的应用和挑战,深入分析其优势与局限性,为软件测试领域的发展提供思路和启示。
|
1月前
|
敏捷开发 测试技术 持续交付
提升软件测试效率与质量的自动化策略
【2月更文挑战第19天】 在快速迭代的软件开发周期中,传统的手动测试方法往往难以满足高效率和高质量的双重要求。本文将探讨如何通过实施自动化测试策略来优化软件测试流程,减少重复劳动,提高测试覆盖率及发现缺陷的速度。我们将分析自动化测试的关键优势,包括时间效率、一致性、可重复性以及其在持续集成环境中的作用,并讨论实施过程中面临的挑战及解决策略。
|
1月前
|
敏捷开发 分布式计算 测试技术
深入理解软件测试中的自动化框架选择与优化策略
【2月更文挑战第29天】 在软件开发的生命周期中,测试环节扮演着至关重要的角色。随着敏捷开发和持续集成的普及,自动化测试成为确保软件质量和加快产品上市速度的关键手段。本文将探讨在构建自动化测试框架时面临的挑战,分析不同类型自动化框架的特点及其适用场景,并提出一系列优化策略,旨在帮助测试工程师提高测试效率,确保测试结果的准确性。
23 0
|
1月前
|
安全 测试技术
现代软件测试中的自动化技术应用及挑战
在当今数字化时代,软件测试的重要性日益凸显。本文探讨了现代软件测试中自动化技术的应用现状和挑战,分析了自动化测试在提高效率、降低成本、增强可靠性等方面的优势,同时也提出了自动化测试所面临的挑战和解决方案。
|
1月前
|
jenkins 测试技术 持续交付
现代软件测试中的自动化工具与挑战
随着软件开发领域的不断发展,自动化测试工具在测试过程中扮演着越来越重要的角色。本文将探讨现代软件测试中自动化工具的应用及面临的挑战,旨在帮助开发人员和测试人员更好地理解和应对自动化测试中的问题。
|
1天前
|
jenkins 测试技术 持续交付
深入探索软件测试中的持续集成与自动化测试实践
【4月更文挑战第27天】 在当今软件开发的快速迭代过程中,持续集成(CI)和自动化测试已成为确保代码质量和加快交付速度的关键因素。本文将探讨如何通过实施持续集成流程,并结合自动化测试策略来优化软件测试工作。我们将分析持续集成的原理、自动化测试的最佳实践以及如何将这些方法应用于实际项目中,旨在为读者提供一套完整的解决方案,以提高软件项目的效率和质量。
9 3
|
18天前
|
敏捷开发 监控 测试技术
深入探索软件测试中的自动化边界
【4月更文挑战第10天】 在现代软件开发过程中,自动化测试已成为提升效率、确保质量的关键手段。然而,随着技术的不断进步和项目需求的多样化,确定自动化的合理边界成为测试工程师面临的重要问题。本文将探讨如何界定自动化测试的有效范围,包括成本效益分析、风险评估与技术选型等方面,并提出一种基于风险和回报权衡的自动化测试策略。
11 0
|
19天前
|
算法 安全 Java
java代码 实现AES_CMAC 算法测试
该代码实现了一个AES-CMAC算法的简单测试,使用Bouncy Castle作为安全提供者。静态变量K定义了固定密钥。`Aes_Cmac`函数接受密钥和消息,返回AES-CMAC生成的MAC值。在`main`方法中,程序对给定的消息进行AES-CMAC加密,然后模拟接收ECU的加密结果并进行比较。如果两者匹配,输出&quot;验证成功&quot;,否则输出&quot;验证失败&quot;。辅助方法包括将字节转为16进制字符串和将16进制字符串转为字节。
|
20天前
|
测试技术
深入理解软件测试中的自动化边界
【4月更文挑战第7天】 在追求快速交付和质量保证的双重压力下,软件测试领域正经历着从手工到自动化的转变。本文旨在探讨自动化测试的有效边界,即哪些场景适合自动化,以及如何界定这些边界以优化测试策略。通过对自动化测试优势、挑战及适用性的分析,文章为读者提供了一个清晰的框架,用于评估和实施自动化测试。