Locust单机多核压测,以及主从节点的数据通信处理!

一、背景

这还是2个月前做的一次接口性能测试,关于locust脚本的单机多核运行,以及主从节点之间的数据通信。

先简单交代下背景,在APP上线之前,需要对登录接口进行性能测试。经过评估,我还是优先选择了locust来进行脚本开发,本次用到了locust的单机多核运行能力,只不过这里还涉及到主从节点之间数据通信。现成的可参考的有效文档甚少,所以还是自己摸着官方文档过河比较靠谱。

顺带提一下,学习框架这种东西最好的教程其实还得是官方文档以及框架源码了,这里贴上locust官方文档链接,需要的可以自行学习:https://docs.locust.io/en/stable/what-is-locust.html

二、代码编写

其实脚本代码的编写一大重点就是如何处理测试数据,不同的测试需求对于测试数据的处理是不同的。比如这次的需求,手机号不能重复。另外考虑到长时间的负载压力,数据量还得足够。

现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036【暗号:csdn999】

最后测试数据还需要处理,那么我使用的测试号段是非真实号码段,测试结束后可以查询对应号段内的手机号,进行相关业务数据的清理。

1. 代码概览

还是老样子,先附上全部代码,然后对其结构进行拆分讲解。

import random
import time
from collections import dequefrom locust import HttpUser, task, run_single_user, TaskSet, events
from locust.runners import WorkerRunner, MasterRunnerCURRENT_TIMESTAMP = str(round(time.time() * 1000))
RANDOM = str(random.randint(10000000, 99999999))
MOBILE_HEADER = {"skip-request-expired": "true","skip-auth": "true","skip-sign": "true","os": "IOS","device-id": "198EA6A4677649018708B400F3DF69FB","nonce": RANDOM,"sign": "12333","version": "1.2.0","timestamp": CURRENT_TIMESTAMP,"Content-Type": "application/json"
}last_mobile = ""
worker_mobile_deque = deque()# 13300120000, 13300160000 新用户注册号段@events.test_start.add_listener
def on_test_start(environment, **_kwargs):if not isinstance(environment.runner, WorkerRunner):mobile_list = []for i in range(13300120000, 13300160000):mobile_list.append(i)mobile_list_length = len(mobile_list)print("列表已生成,总计数量:", mobile_list_length)worker_count = environment.runner.worker_countchunk_size = int(mobile_list_length / worker_count)print(f"平均每个worker分得的手机号数量:{chunk_size}")for i, worker in enumerate(environment.runner.clients):start_index = i * chunk_sizeif i + 1 < worker_count:end_index = start_index + chunk_sizeelse:end_index = len(mobile_list)data = mobile_list[start_index:end_index]environment.runner.send_message("mobile_list", data, worker)def setup_mobile_list(environment, msg, **kwargs):len_msg_data = len(msg.data)print(f"worker收到的master传来的数据号段:{msg.data[0]} ~ {msg.data[len_msg_data-1]}")global worker_mobile_dequeworker_mobile_deque = deque(msg.data)@events.init.add_listener
def on_locust_init(environment, **_kwargs):if not isinstance(environment.runner, MasterRunner):environment.runner.register_message('mobile_list', setup_mobile_list)class VcodeLoginUser(TaskSet):# wait_time = between(5, 5)@taskdef vcode_login(self):test_mobile = worker_mobile_deque.popleft()print("当前获取的手机号:", test_mobile)# print("当前队列大小:", len(worker_mobile_deque))global last_mobilelast_mobile = test_mobilewith self.client.post("/g/sendMobileVcode",headers=MOBILE_HEADER,json={"busiType": "login", "mobile": str(test_mobile)}) as send_response:try:send_response_json = send_response.json()if send_response_json["message"] == "success":params = {"mobile": str(test_mobile), "vcode": "111111"}# print(test_mobile, "登录请求参数:", params)with self.client.post("/g/vcodeLogin",json=params,headers=MOBILE_HEADER,catch_response=True) as login_response:# print(login_response.json)login_response_json = login_response.json()if login_response_json["message"] != "success":login_response.failure("message not equal success")elif login_response_json["code"] != 0:login_response.failure("code not equal 0")elif login_response_json["data"]["rId"] == "":login_response.failure("rid is null")elif login_response_json["data"]["mobile"] != str(test_mobile):login_response.failure("mobile is error,入参手机号{},返回的手机号{}".format(test_mobile, login_response.json()["data"]["mobile"]))# print(test_mobile, "请求结果:", login_response.json())else:send_response.failure("{} send code fail".format(test_mobile))except Exception as e:send_response.failure("send code fail {}".format(e))@events.test_stop.add_listenerdef on_test_stop(environment, **kwargs):print("脚本结束")print("当前队列大小:", len(worker_mobile_deque))print("最后的手机号:", last_mobile)class LocustLogin(HttpUser):tasks = [VcodeLoginUser]host = "https://qa.test.com"if __name__ == '__main__':run_single_user(LocustLogin)

2. 代码拆解-要加必要的断言

首先是基于locust开发的http请求的脚本大结构是不变的,依旧是两大块:HttpUserTaskSet,这里不再对其讲解了,大伙看下官方文档就明白了。

接下来就是类VcodeLoginUser,可以看到在这里面是定义了单个用户的详细动作。注意这里要加上必要的断言。否则仅靠框架的非200外的错误断言还是不够的。

比如我这里关注登录成功后的几个必要字段:coderIdmobile,这些一定是要符合断言的才可以。

果不其然,压测过程中就发现了并发情况下会出现的问题:入参手机号是a,接口返回的手机号是b。并发量越大错误越多。如果我只断言code=0,那么这个问题就不容易发现了,虽然接口返回的code都是成功的,但是业务上已经存在错误了。

...with self.client.post("/g/sendMobileVcode",headers=MOBILE_HEADER,json={"busiType": "login", "mobile": str(test_mobile)}) as send_response:try:send_response_json = send_response.json()if send_response_json["message"] == "success":params = {"mobile": str(test_mobile), "vcode": "111111"}# print(test_mobile, "登录请求参数:", params)with self.client.post("/g/vcodeLogin",json=params,headers=MOBILE_HEADER,catch_response=True) as login_response:# print(login_response.json)login_response_json = login_response.json()if login_response_json["message"] != "success":login_response.failure("message not equal success")elif login_response_json["code"] != 0:login_response.failure("code not equal 0")elif login_response_json["data"]["rId"] == "":login_response.failure("rid is null")elif login_response_json["data"]["mobile"] != str(test_mobile):login_response.failure("mobile is error,入参手机号{},返回的手机号{}".format(test_mobile, login_response.json()["data"]["mobile"]))# print(test_mobile, "请求结果:", login_response.json())else:send_response.failure("{} send code fail".format(test_mobile))except Exception as e:send_response.failure("send code fail {}".format(e))
...

3. 代码拆解-单机多核处理

接下来就是重点了,如何在单台机器上用到多cpu。最开始的时候我忽略了这点,后来发现负载上不去,一打开资源监视器才发现只有1个cpu在满负载运行。

这里示意图仅供参考,我的win笔记本是12c的。

因为Locust是单进程的,不能充分利用多核CPU,于是需要我们压力机上开启一个master进程,然后再开启多个slave进程,组成一个单机分布式系统即可。

开启的方式也很简单:

# 开启 master 
locust -f locustfile.py --master# 开启 slave
locust -f locustfile.py --slave

这里我们开启 slave 节点的时候可以开启对应多个命令行窗口,当时没截图,借用网上的图片示意一下:

开启后,你的web界面就可以实时看到当前启动的节点数了。

4. 代码拆解-处理主从节点数据通信

开启主从节点倒是很容易,测试数据就需要针对性进行处理了。

因为我的测试登录用的手机号不可以重复,所以要保证不同 slave 节点上同时运行的代码产生的手机号都不可以重复。

继续扒了下官方文档,发现可以通过增加事件监听器来实现我的需求。

这里我加了三个监听器分别来处理不同的事情:

  • @events.init.add_listener:在locust运行初始化的时候执行
  • @events.test_start.add_listener: 在测试代码开始运行的时候执行
  • @events.test_stop.add_listener: 在测试代码结束运行的时候执行

@events.test_start.add_listener 首先,在@events.test_start.add_listener里,我主要处理全量数据的生成,以及把这些手机号平均分配给生成的 slave 节点。

@events.test_start.add_listener
def on_test_start(environment, **_kwargs):if not isinstance(environment.runner, WorkerRunner):mobile_list = []for i in range(13300120000, 13300160000):mobile_list.append(i)mobile_list_length = len(mobile_list)print("列表已生成,总计数量:", mobile_list_length)worker_count = environment.runner.worker_countchunk_size = int(mobile_list_length / worker_count)print(f"平均每个worker分得的手机号数量:{chunk_size}")for i, worker in enumerate(environment.runner.clients):start_index = i * chunk_sizeif i + 1 < worker_count:end_index = start_index + chunk_sizeelse:end_index = len(mobile_list)data = mobile_list[start_index:end_index]environment.runner.send_message("mobile_list", data, worker)

注意这里最后一行中定义的mobile_list,需要定义一个对应函数来接收这个数据。

def setup_mobile_list(environment, msg, **kwargs):len_msg_data = len(msg.data)print(f"worker收到的master传来的数据号段:{msg.data[0]} ~ {msg.data[len_msg_data-1]}")global worker_mobile_dequeworker_mobile_deque = deque(msg.data)

这样,不同的 slave 节点脚步分配到的手机号段就是不同的了,解决测试数据重复的问题。

另外,我定义另一个全局变量worker_mobile_deque,这样不同的 slave 节点接收的数据就可以放到队列里,运行的时候从队列里面取,用一个少一个,直到队列里的数据用完。

@events.init.add_listener 接着就是在@events.init.add_listener里要注册上面定义的数据字段和处理函数。

@events.init.add_listener
def on_locust_init(environment, **_kwargs):if not isinstance(environment.runner, MasterRunner):environment.runner.register_message('mobile_list', setup_mobile_list)

@events.test_stop.add_listener 最后,在@events.test_stop.add_listener这里可以做一些后置处理,我是简单起见,只是记录输出了本次测试用到了哪个号码段,这样我下次运行脚本的时候可以从后面的数据开始,最大化测试数据的使用,不浪费。

    @events.test_stop.add_listenerdef on_test_stop(environment, **kwargs):print("脚本结束")print("当前队列大小:", len(worker_mobile_deque))print("最后的手机号:", last_mobile)

三、小结

脚本调试完后可以稳定运行,接下来就是测试的过程了,进行了服务器单节点、多节点负载能力的测试,水平拓展能力的测试,以及服务动态扩容、长时间高负载测试。测试的角度观察测试报告,服务各项指标的情况。只不过涉及到开发端,调优分析的工作并未能参与很多。不过大概还是那些常见问题,后续有机会可以再单独分享了。

从使用角度来看,locust深得我爱,比起 jemter真的太轻便了,代码灵活度也非常高,单机负载能力也是响当当的,这点比jemeter强太多了。我这个项目不需要非常高的量,所以单机只用了8c就够了。如果有小伙伴需要非常高的并发,locust 也支持多机器分布式,进一步扩大并发能力。

END今天的分享就到此结束了!点赞关注不迷路~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/186350.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用内网穿透实现无公网ip环境访问VScode远程开发

文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…

msvcp140.dll的解决方法有哪些。详细解析五种可以修复msvcp140.dll丢失的方法

引言&#xff1a; 在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。那么&#xff0c;什么是msvcp140.dll文件&#xff1f;它的作用是什么&#xff1f;当它丢失时会对电脑产生什么影响&#xff1f;本文将详细介绍…

软件测试jmeter基本使用

1安装与配置 1.jdk下载 下载地址&#xff1a;https://www.oracle.com/java/technologies/downloads/#jdk18-windows&#xff08;压缩包中会给&#xff09; 2.jmeter下载 Apache JMeter - Download Apache JMeter&#xff08;压缩包中有&#xff09; 3.操作教学 打开软件后新…

C# PIE-SDK二次开发界面汉化方法

那些最好的程序员不是为了得到更高的薪水或者得到公众的仰慕而编程&#xff0c;他们只是觉得这是一件有趣的事情&#xff01; C# PIE-SDK二次开发界面汉化方法 &#x1f340;前言&#x1f338;配置方法&#x1f355;拷贝语言包文件夹&#x1f354;增加窗体代码&#x1f35f;运行…

什么是PDN的交流阻抗?

什么是PDN的交流阻抗&#xff1f; 在电力电子领域&#xff0c;PDN&#xff08;Power Distribution Network&#xff09;的交流阻抗是一个重要的概念&#xff0c;它反映了PDN在交流电源和负载之间传输电能的能力。了解PDN的交流阻抗对于优化电源设计、提高系统性能和可靠性具有重…

SSM SpringBoot vue考勤信息管理系统

SSM SpringBoot vue考勤信息管理系统 系统功能 登录 注册 个人中心 部门信息管理 上班时间管理 考勤信息管理 员工信息管理 签到管理 请假信息管理 加班申请管理 出差申请管理 开发环境和技术 开发语言&#xff1a;Java 使用框架: SSM(Spring SpringMVC Mybaits)或Spring…

珠宝模具3d仿真沉浸式交互展示更易分享传播

3D云展会经过近几年的蓬勃发展&#xff0c;迅速受到参展企业和客户的多方认可和支持&#xff0c;那么随着市场再度恢复&#xff0c;各种展会络绎不绝&#xff0c;想要快速打造一个逼真的线上3D云展会成为企业刚需。3D云展会线上搭建平台是web3d开发公司深圳华锐视点根据领先的三…

CSS 在性能优化方面的实践

前言 CSS&#xff08;层叠样式表&#xff09;是一种用于描述网页外观和格式的语言。随着网页变得越来越复杂&#xff0c;CSS文件的大小也随之增加&#xff0c;这可能会对网页性能产生负面 .box {width: 100px;height: 100px;transition: transform 0.3s; }.box:hover {transf…

全网最最全的Jmeter接口测试:jmeter_逻辑控制器_交替控制器Jmeter(22):jmeter_逻辑控制器_交替控制器

交替控制器 该控制器包含的取样器步骤在每次循环中交替执行 交替控制器指每次运行一次时在交替控制器下的采样器只执行一个&#xff1b;如下图&#xff1a; 忽略子控制模块&#xff1a;如果勾选此项,交替控制器将子控制器像单一请求元素一样&#xff0c;一次 只允许一个请求/…

Java中的JMX的使用

文章目录 1. 定义和存在的意义2. 架构2.1 Instrumentation2.2 JMX Agent2.3 Remote Management 3. 启动和连接3.1 注册MBean3.2 有两个方式启动JMX Agent3.3 Remote Management(客户端) 4. MBeanServerConnection使用4.1 列出所有的MBean4.2 列出所有的Domain4.3 MBean计数4.4 …

算法:Java计算二叉树从根节点到叶子结点的最大路径和

要求从根节点到叶子结点的最大路径和&#xff0c;可以通过递归遍历二叉树来实现。对于二叉树中的每个节点&#xff0c;我们都可以考虑包含该节点的最大路径和。在递归的过程中&#xff0c;我们需要不断更新全局最大路径和。 具体的思路 递归函数设计&#xff1a; 设计一个递归函…

常用的设计模式

常用的设计模式&#xff1a; 一、单例模式 java中单例模式是一种常见的设计模式&#xff0c;单例模式的写法有好几种&#xff0c;这里主要介绍三种&#xff1a;懒汉式单例、饿汉式单例、双重检查锁定 1、单例模式有以下特点&#xff1a;   a、单例类只能有一个实例。   b…

36.JavaScript补完计划:typescript

点赞收藏加关注&#xff0c;你也能住大别墅&#xff01; 一、什么是typescript 二、应用场景 我认为JavaScript的特点就是在于它强大的延展性&#xff0c;不仅蔓延到了后端&#xff0c;而且也逐渐成为代码世界无法被忽视的存在。那么&#xff0c;编写js代码时我们都会经常遇到…

uniapp项目打包h5,支持文件协议,使用vconsole调试移动端

uniapp项目需要打包h5&#xff0c;并且需要嵌套到一个原生的移动端项目中&#xff0c;需要支持文件协议能直接访问 打包设置 设置./基础路径 引入vconsole调试移动端 <script src"https://unpkg.com/vconsole/dist/vconsole.min.js"></script>可以将…

【Android Jetpack】Lifecycle 感知生命周期

文章目录 背景示例LifeCycle的原理LifecycleOwner自定义LifecycleOwnerLifecycleObserver 示例改进使用LifecycleService解耦Service与组件整个应用进程的生命周期ProcessLifecycleOwner 背景 在Android应用程序开发中&#xff0c;解耦很大程度上表现为系统组件的生命周期与普…

[跑代码]BK-SDM: A Lightweight, Fast, and Cheap Version of Stable Diffusion

Installation(下载代码-装环境) conda create -n bk-sdm python3.8 conda activate bk-sdm git clone https://github.com/Nota-NetsPresso/BK-SDM.git cd BK-SDM pip install -r requirements.txt Note on the torch versions weve used torch 1.13.1 for MS-COCO evaluation…

windows远程桌面登录,提示:“出现身份验证错误,要求的函数不受支持”

问题&#xff1a; windows登录远程桌面&#xff0c;提示&#xff1a;“出现身份验证错误&#xff0c;要求的函数不受支持”&#xff0c;如下图&#xff1a; 问题原因&#xff1a; windows系统更新&#xff0c;微软系统补丁的更新将 CredSSP 身份验证协议的默认设置进行了调…

Windows + docker + python + vscode : 使用容器docker搭建python开发环境,无需本地安装python开发组件

下载docker for Windows docker window下载 如果没有翻墙工具&#xff0c;可以该网盘中的docker 链接&#xff1a;https://pan.baidu.com/s/11zLy3e5kusZR-4m_Fq_cqg?pwdesmv 提取码&#xff1a;esmv 安装docker docker的安装会重启电脑&#xff0c;不要惊讶&#xff0c;且…

Unity 注释的方法

1、单行注释&#xff1a;使用双斜线&#xff08;//&#xff09;开始注释&#xff0c;后面跟注释内容。通常注释一个属性或者方法&#xff0c;如&#xff1a; //速度 public float Speed;//打印输出 private void DoSomething() {Debug.Log("运行了我"); } …

构建智能预约体验:深度解析预约系统源码的代码精髓

随着数字化时代的发展&#xff0c;预约系统在各行业中扮演着越来越重要的角色。本文将深入研究预约系统源码&#xff0c;通过代码示例分析其技术要点&#xff0c;为开发者提供实用的指导&#xff0c;助力构建智能、高效的预约体验。 技术栈综述 预约系统源码采用了现代化的技…