Locust-入门

背景:

目前网上的教程基本都是1.0之前的,locust丛1.0版本就发生了较多的变化,网上的教程基本不可用了。本文基于locust最新版本2.5.1,作为笔记也作为入门教程分享。

(备注:只讲 框架的使用 ,不涉及性能测试理论知识)

一、什么是Locust

Locust 是一种易于使用、可直接使用pyhton编写脚本运行且可扩展的性能测试工具。

 

二、特点

  • 直接使用python 编写测试脚本    
  • 支持分布式和可扩展-支持数十万并发用户
  • 提供友好的web界面
  • 可以测试任何系统
  • 小且灵活

 

locust 的web 运行界面

locust 提供了友好的web界面用于监控性能测试过程中的相关数据 

启动页

 

 测试监控界面

 

type 请求类型  
Name 请求名称 这个可以自定义,传name参数即可
Requests 当前已完成的请求数量  
Fails 失败的请求数量  
Requests 当前已完成的请求数量  
Median 响应时间的中间值,即50%的响应时间在这个数值范围内,单位为毫秒  
90%ile 90%的响应时间在正态分布平均值下方,即小于这个值  
Min 最小响应时间,单位为毫秒  
Max 最大响应时间,单位为毫秒  
average Size 平均每个请求的数据量,单位为字节  
current RPS 每秒钟处理请求的数量

 

 

 

RPS(TPS)

 

响应时间 

 

 

Users

 

测试任务统计

 

支持下载测试报告

 

 进程信息,这里启动了5个进程,用户数为500,被平均分给了5个进程,

 

三、编写任务

名词解释:

  任务:简单的理解就是,你想要你脚本的虚拟用户去做哪些事,比如请求某一个接口,或者执行某一个事件。

  用户:可以理解为,执这个任务的实例主体,或者在locust 中,也可以认为是一群蝗虫

 

  旧版本使用的是TaskSet进行任务或者请求编写,然后在使用HttpLocust进行任务或者请求声明,以及基础属性配置,一般会进行思考时间、host、task_set设置任务类等;新版本将HttpLocust重写成User,并且HttpUser继承于User,所以推荐使用HttpUser进行任务声明;另外TaskSet中的task_set已被删除,如果仍要使用TaskSet,则需要在HttpUser中使用tasks进行存储

 

TaskSet 类 进行任务编写

TaskSet是定义用户将执行的一组任务的类,然后通过虚拟用户调用并执行这一组任务

单个任务集合:  

复制代码
from locust import TaskSet, task,User
import os

''' 
通过TaskSet 编写任务
'''
class Task_1(TaskSet):

    @task
    def task1(self):
        print('task1----------')

    @task
    def task2(self):
        print('task2----------')

    @task
    def task3(self):
        print('task3----------')

class task_conf(User):
    tasks = [Task_1]

if __name__ == '__main__':
    os.system('locust -f TaskSet定义用户任务.py --web-host "127.0.0.1"')
复制代码

多个任务集合

切记 不要忘记调用调用 self.interrupt() 以跳出当前任务集

复制代码
from locust import TaskSet, task,User
import os

''' 
TaskSet类定义了每个用户的任务集合,
注意:如果存在多个任务集合,需要在每个任务集合中调用interrupt()方法,
否则用户一旦进入到这个用户,则无法跳出这个任务集合(会一直执行这个任务集)
'''
class Task_1(TaskSet):

    @task
    def task1(self):
        print('task1----------')

    @task
    def task2(self):
        print('task2----------')

    @task
    def task3(self):
        print('task3----------')

    #调用interrupt() 非常重要,否则用户将难以自拔
    @task
    def logout(self):
        self.interrupt()

class Task_2(TaskSet):

    @task
    def task21(self):
        print('task21----------')

    @task
    def task22(self):
        print('task22----------')

    @task
    def task23(self):
        print('task23----------')

    @task
    def logout(self):
        self.interrupt()


class task_conf(User):
    
    tasks = [Task_1,Task_2]

if __name__ == '__main__': os.system('locust -f TaskSet定义用户任务_多个.py --web-host "127.0.0.1"')
复制代码

 

User类 编写任务 

User 执行测试任务的主体类,用户的行为由其任务定义

复制代码
from locust import TaskSet, task,User
import os

''' 
通过TaskSet 定义任务
'''
class Task_1(User):

    @task
    def task1(self):
        print('task1----------')

    @task
    def task2(self):
        print('task2----------')

    @task
    def task3(self):
        print('task3----------')

if __name__ == '__main__':
    os.system('locust -f User定义用户任务.py --web-host "127.0.0.1"')
复制代码

 

任务属性

测试开始时,locust将为每一个模拟用户创建一个User类的实例,每个实例都在他们自己的微线程中执行,当这些用户运行时,开始选中要执行的任务,休眠一段时间,然后选一个新的任务,继续执行,以此类推。

这里我们为用户添加任务的方式有两种

  • 使用@task 装饰器
  • 设置tasks属性

@task

通过@task 定义用户任务 任务 ,同时还可以通过@task(int) 设置每个任务的权重
复制代码
from locust import task,User
import os


# 通过@task 定义用户任务 任务 ,同时还可以通过@task(int) 设置每个任务的权重
class task_2(User):

    @task(2)
    def task1(self):
        print('执行任务1')

    @task(2)
    def task2(self):
        print('执行任务2')

    @task(1)
    def task3(self):
        print('执行任务3')


if __name__ == '__main__':
    os.system('locust -f task装饰器声明任务.py --web-host="127.0.0.1"')
复制代码

 

tasks 

tasks属性可以是一个 Task 列表,也可以是一个 {'Task':int} 的dict, Task可以是 一个测试方法或者函数,也可以是任务集合TakSet类

以User类为例

复制代码
from locust import User
import os

''' 
继承TaskSet,不按顺序执行任务,
'''

# 通过tasks 定义测试任务
class task_1(User):

    def task1(self):
        print('执行任务1')

    def task2(self):
        print('执行任务2')

    def task3(self):
        print('执行任务3')

    # 通过tasks 定义测试任务 并设置对应任务执行权重
    tasks = [task1, task2, task3]
    # tasks = {task1:1,task2:2,task3:2}

if __name__ == '__main__':
    os.system('locust -f tasks属性声明用户任务.py --web-host="127.0.0.1"')
复制代码

 

任务标记@tag

可以通过标记的方式,执行负载测试时,让虚拟用户只执行指定的测试任务

复制代码
from locust import User,tag

'''
设置tag,用于设置用户 执行指定标记的任务
'''
class myUser(User):

    @tag('回归')
    def task_1(self):
        print('回归任务')
    @tag('预发')
    def task_2(self):
        print('任务2')

    @tag('回归','预发')
    def task_3(self):
        print('回归任务+预发任务')

    tasks = [task_1,task_2,task_3]

if __name__ == '__main__':
    import os
    os.system('locust -f locust_user_tag.py --tags 回归 --web-host="127.0.0.1"')
复制代码

 

 User类的一些通用属性

Locust 将为每个用户生成一个 User 类的实例。用户类可以定义一些通用属性。

wait_time 属性:

用于模拟用户每次执行任务后的延迟时间(等同思考时间),如果未指定,则下一个任务将在完成后立即执行。

  • constant(x)  固定时间
  • between(x,y)     最小值和最大值之间的随机时间
  • constant_packing(x)  每x秒 最多运行一次任务
  • constant_throughtput(x)     每秒最多运行x次
constant
复制代码
from locust import constant, task,User
import os

''' 
wait_time = constant() 等待一个固定的时间
'''
class Task_1(User):

    @task
    def task1(self):
        print('task1----------')

    @task
    def task2(self):
        print('task2----------')

    @task
    def task3(self):
        print('task3----------')

    # 用户执行每个任务,会固定等待5s
    wait_time = constant(5)

if __name__ == '__main__':
    os.system('locust -f task_wait_constant.py --web-host "127.0.0.1"')
复制代码
between
复制代码
from locust import between, task, User
import os

''' 
wait_time = between() 等待一个最小值和最大值之间的随机时间 不含最大时间
'''

class Task_1(User):

    @task
    def task1(self):
        print('task1----------')

    @task
    def task2(self):
        print('task2----------')

    @task
    def task3(self):
        print('task3----------')

    # 用户执行每个任务,会固定等待[1,5) 秒
    wait_time = between(1,5)


if __name__ == '__main__':
    os.system('locust -f task_wait_between.py --web-host "127.0.0.1"')
复制代码

 

weight权重属性

用于设置特定类型的用户的 权重值,在未设置的情况下,locust将为每个用户类生产相同数量的用户实例

比如app用户是web用户的3倍

复制代码
from locust import User

# app用户数量是web用户数量的3倍
class appUser(User):
    weight = 3
 
    def task_1(self):
        print('app任务1')

    def task_2(self):
        print('app任务2')

    tasks = [task_1,task_2]

class webUser(User):
    weight = 1

    def task_A(self):
        print('web任务1')

    def task_B(self):
        print('web任务2')

    tasks = [task_A, task_B]

if __name__ == '__main__':
    import os
    os.system('locust -f locust_user_weight.py --web-host="127.0.0.1"')
复制代码

 

 host 主机属性:

用于加载主机的url 前缀,(比如:http://baidu.com)

也可以在 webui中指定,或者命令参数中指定--host

复制代码
from locust import task,HttpUser
import os

'''
主机属性 host 
'''

class task_s(HttpUser):

    host = 'http://wthrcdn.etouch.cn'
    @task
    def task_1(self):
        url = '/weather_mini?city=杭州'
        # body = {'city':'杭州'}
        res = self.client.request(method='get',url=url)
        print(res.json())

if __name__ == '__main__':
    os.system('locust -f locust_host.py --web-host="127.0.0.1"')
复制代码

 

前后置方法 on_start和on_stop方法

用户将 on_start在它开始运行时调用它的方法

on_stop在它停止运行时调用它的方法。

复制代码
from locust import User,constant

'''
   每个虚拟用户 执行前
   会执行一次该方法(在一次测试结束后,只会执行一次)
'''

class myuser(User):
    wait_time = constant(2)
   
    def on_start(self):
        print('登录')
    def on_stop(self):
        print('登出')

    def task_1(self):
        print('任务1')


    tasks = [task_1]

if __name__ == '__main__':
    import os
    os.system('locust -f locust_start_stop.py --web-host="127.0.0.1"')
复制代码

 

 

HttpUser类

HttpUser是最常用的 User ,它通过client 发送Http请求,client是httpsession的一个实例(requests模块的session),故在locust中,通过client 发送的请求,具有天然的保持会话能力。

发送http请求

复制代码
from locust import HttpUser,task
import os

class TaskA(HttpUser):
#登录 def on_start(self): url = 'https://www.processon.com/login/quick_login' data = {'type': 'account_login', 'login_email': 12345678901, 'login_password': '123456', } r = self.client.request('post', url=url, data=data) #登录成功调用个人信息接口 @task def test_login(self): url = 'https://www.processon.com/setting/account' r = self.client.request('post', url=url) print(r.text) if __name__ == '__main__': os.system('locust -f http_通过session请求.py --web-host="127.0.0.1"')
复制代码

 

响应检查

HttpUser 支持设置响应检查点

使用catch_response参数、with语句和对response.failure()  标记为失败

复制代码
from locust import HttpUser, task
import os
from json import JSONDecodeError
import jsonpath

class task_s(HttpUser):

    @task
    def task_1(self):
        get_url = 'http://wthrcdn.etouch.cn/weather_mini?city=杭州'
        with self.client.request(method='get', url=get_url,catch_response=True) as response:
            try:
                # 获取响应内容的status 字段值
                status = response.json()['status']
                # 获取响应时间
                # rt = response.elapsed.total_seconds()
                # print(rt)
                # 如果status为1000则为成功,否则为失败
                if status == 1000:
                    response.success()
                    # if rt <= 0.001:
                    #     response.success()
                    # else:
                    #     response.failure('响应超时')
                else:
                    response.failure('非法的参数')
            except JSONDecodeError:
                response.failure('Response could not be decoded as JSON')

if __name__ == '__main__':
    os.system('locust -f locust_HttpUse_检查响应.py --web-host="127.0.0.1"')
复制代码

 

FastHttpUser (locust大杀器)

Locust 的默认 HTTP 客户端使用python-requests,而requests库是一个同步阻塞库(每个请求发送完成之后都在等待响应的回来,这在高并发场景下是致命的)

正因为如此,Locust 还附带FastHttpUser使用geventhttpclient代替。它提供了一个非常相似的 API,并且使用的 CPU 时间显着减少,通常情况下将给定硬件上每秒的最大请求数增加了 5 到 6 倍。

官方文档FastHttpUser 和 HttpUser 比较
使用 FastHttpUsers 的测试将能够在每个核心每秒执行接近 5000 个请求,使用 HttpUser 大约 850 个

使用上:只需继承 FastHttpUser 就可以了,其他和 HttpUser 没有什么太大的差别

复制代码
from locust import task,FastHttpUser
import os
import logging as log

class task_s(FastHttpUser):
    host = 'http://wthrcdn.etouch.cn'
    @task
    def task_1(self):
        get_url = '/weather_mini?city=杭州'
        self.client.request(method='get', path=get_url)
        # with self.client.request(method='get', path=get_url,catch_response=True) as response:
        #     status = response.json()['status']
        #     if status ==1000:
        #         response.success()
        #
        #     else:
        #         response.failure('请求失败')


if __name__ == '__main__':
    os.system('start locust -f 通过FastHttpUser多进程发送请求.py  --master --web-host="127.0.0.1"')
    for i in range(8):
            os.system('start locust -f 通过FastHttpUser多进程发送请求.py  --worker')
复制代码

并发数1500,tps在9000左右

 

 对比jmeter,并发数1500, tps在 8000左右

 

 

四、运行方式

命令参数运行

复制代码
#进入当前模块文件目录,直接输入
locust

#非当前文件目录
locust -f  xx/xx.py 

#指定host
locust -f  xx/xx.py  --host=https://www.cnblogs.com

#指定locust webui 界面地址,
locust -f  xx/xx.py  --web-host ="127.0.0.1"

#不带ui界面运行,
locust -f  xx/xx.py  --headless -u 1000 -r 100 --run-time 5m
--headless 没有webui的情况下运行
-u  要生成的用户数
-r   每秒启动的用户数
--run-time 5m 设置测试执行时间 这里是5分钟,时间到了就会停止运行

#多进程运行 主模式启动 8089端口用于web界面 ,5557用于从机交互 locust -f xx/xx.py --master 启动一个进行执行 locust -f xx/xx.py --worker --master-host=192.168.0.14 (如果单机器运行,则可以省略后面的参数 --master-host=xxx.xxx.xxx.xxx)
复制代码

 配置文件方式运行

locust 支持配置文件方式运行

(本质上也是命令行运行,只不过是把命令参数写在了配置文件中,防止每次输的时候 写错)

默认情况下,locust在~./.locust.conf 和 ./locust.conf ,还可以使用 --config 来指定配置文件运行

复制代码
# -*- coding: UTF-8 -*
'''
# locust 默认读取配置文件的优先级顺序(覆盖读取)
# ~/locust.conf -> ./locust.conf ->(file specified using --conf)
# 注意:如果同时存在 两个配置文件,locust.conf 和 diy.conf
# 此时使用 locust --config = diy.conf 读取的将还是locust.conf
'''

# 要运行的py文件
locustfile = ./my_locust_file.py
# 设置是否带webui运行
headless = false
# 设置weiui的地址
web-host= 127.0.0.1
# 设置 host
host = http://wthrcdn.etouch.cn
# 设置虚拟用户数
users = 500
# 设置每秒增加的用户数
spawn-rate = 10
# 设置运行时间,满足设置的运行时间后,将停止运行
run-time = 1m
复制代码

 

 

 

 

 

 

  

 

posted @   昆虫白  阅读(3711)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示