服务端性能测试实战3-性能测试脚本开发
bigegpt 2025-07-03 12:29 3 浏览
前言
在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:
- 阶段一:了解各个接口的入参、出参,使用Python代码模拟前端发送http请求,重点是通过代码调用串联起各个接口,了解接口层面的基本流程,也为后续模拟压测打好基础;
- 阶段二:将代码的接口实现转移到性能测试工具中来实现,跑通流程,重点是做好细节实现以及接口关联设计
一、Python脚本开发
1.加解密函数编写
加密:GET、DELETE请求参数不需要加密,POST请求需要对参数进行加密;
解密:每个请求返回的都是密文,所以在接口调用时要对密文进行解密。
由于加解密逻辑较为复杂,且不具有普适性,因此,此处只作简单说明,不介绍具体逻辑实现。看似简单的两个加密、解密函数,其实还是花了不少时间和精力才搞出来。
def encrypt_aes(clear_text, aes_key):
"""
使用 AES 加密明文
:param clear_text: 明文
:param aes_key: AES 密钥
:return: 加密后的 Base64 编码字符串
"""
pass
def decrypt_aes(encrypt_text, aes_key):
"""
使用 AES 解密密文
:param encrypt_text: 密文(Base64 编码字符串)
:param aes_key: AES 密钥
:return: 解密后的字符串
"""
pass
2.封装接口
以快速创建接口为例:
def quick_start_meeting(self, auth_token, c_token, aes_key, tenant_id):
"""
快速创建会议
:param auth_token: 在头里用
:param c_token: 在头里用
:param topic: 会议主题
:param tenantId: 租户ID
:param scheduleTime: 日期时间, int
:param timezone: 时区
:return: 会议信息
"""
path = "/meeting/start"
headers = {"authtoken": auth_token, "Content-Type": "application/json", "ctoken": c_token, "ec": self.ec,
"ev": self.ev, "user-agent": self.user_agent, "deviceid": self.device_id}
data = {"topic": "GG Meeting" + str(random.randint(1000, 9999)), "scheduleTime": self.timestamp,
"timezone": "Asia/Shanghai", "tenantId": tenant_id}
enc_data = encrypt_aes(json.dumps(data), aes_key)
response = requests.post(self.url + path, headers=headers, json={"data": enc_data})
dec_data = decrypt_aes(json.dumps(eval(response.text)["data"]), aes_key)
print("会议创建成功")
return dec_data
代码解释:
- headers:组装请求头
- data:组装创建会议所需的参数;
- 调用加密方法encrypt_aes,传入会议参数和Key进行加密操作;
- 发起POST请求
- 调用解密方法decrypt_aes,传入处理过的返回值和Key进行解密操作;
- 返回解密后的响应体;
3.调用接口
把所有的接口封装完,我们来调用接口验证是否能调用通。
可以看到接口调用成功,返回了会议id、会议Number等信息。
4.参数构造及数据关联设计
接口参数通常会涉及到三种类型:
- 灵活传递参数的字段:这类参数需要做关联设计,这样可以保证数据是“活”的。在测试过程中,每个环境的数据都不一样,我们不能用写死的数据来发起请求,例如某个ID字段,在A环境有,但是B环境没有。每次都手动改数据的话,也不现实。从上一个接口取值传递给下一个接口,是接口测试过程中常用的数据关联做法,这样可以保证数据是实时且有效的。
- 唯一性校验的字段:例如商品编号、身份证号码等。对于这类字段,我们尽量使用自生成的数据,保证每次提交的数据都是唯一的,不会造成参数重复之类的冲突,同时也方便日志定位。尽量不要从CSV这类的文件中读取,因为只能读取一轮,第二轮再读取又会提交相同的数据了,同时也会增加操作的复杂度。
- 没有唯一性校验的字段:例如商品价格、人员年龄。这类字段没有唯一性校验,可以固定写死。
5.验证接口流程
先在代码中确保每个接口都能正常调用,再通过参数传递的方式将各个接口串联起来。接口流程畅通后,接下来就可以把各个接口腾到Jmeter中来实现。
二、Jmeter脚本开发
不同于在Python代码中实现,在Jmeter工具中,绝不仅仅是添加一些HTTP请求、把各个接口腾过来这么简单;首要问题就是解决参数的加解密问题,这一步搞不定其他都是白瞎。
我最初的想法是在Jmeter工具中使用前后置处理器调用Python加解密函数来对参数进行加密、返回值解密,但是折腾了大半天发现Jmeter对于Python代码的调用并不能很好的支持(虽然也可以安装Jython插件、在JSR223后置处理器中编写Python代码,但Jython插件只支持Python2.7的语法,后续可能会面临一系列麻烦),所以最后放弃了这个方案。
Jmeter是Java写的,所以Jmeter的前后置处理器天然对于Java语言支持最好。所以可以在JSR223后置处理器中调用Java加解密函数,来实现参数、返回值的加解密。
1.解决参数加解密问题
想要在JSR223后置处理器中调用Java加解密函数,来实现参数、返回值的加解密。必然要导入这个加解密的方法。具体实现思路是(这种方式适用于引用所有自定义的Java方法):
- 用Java语言编写加解密工具类
- 编译Java工具类代码,生成class文件
- 将class文件打为jar包
- 将jar包放到Jmeter指定扩展目录
- 在前后置处理器中导入加解密工具类
- 前置处理器中调用工具类中的加密方法对组装好的参数进行加密
- 后置处理器中调用工具类中的解密方法对返回密文进行解密
① 编写 AESUtil.java加解密工具类
package com.csb.meeting.crypto.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class AESUtil {
// 加密算法RSA
public static final String KEY_ALGORITHM = "AES";
// 编码方式
public static final String CODE_TYPE = "UTF-8";
// 填充类型 AES/ECB/PKCS5Padding AES/ECB/ISO10126Padding
public static final String AES_TYPE = "AES/ECB/PKCS5Padding";
/**
* 加密
*
* @param clearText 明文
* @param aesKey AES秘钥
* @return 加密串
*/
public static String encryptAes(String clearText, String aesKey) {
...
}
/**
* 解密
*
* @param encryptText 密文
* @param aesKey AES秘钥
* @return 解密串
*/
public static String decryptAes(String encryptText, String aesKey) {
...
}
}
② 编译 AESUtil.java代码,生成class文件
确保 AESUtil.java 代码能成功编译。在命令行先使用 javac 进行编译,生成.class目录及文件:
javac -d . AESUtil.java
此命令会在当前目录下生成
com/meeting/crypto/utils/AESUtil.class 文件。
③ 打包class文件为jar包
再使用jar命令将.class目录及文件打成包jar包:
jar cf AESUtil.jar com
④ 将编译后的jar包添加到 JMeter 指定扩展路径
把生成的AESUtil.jar复制到 JMeter 的 lib/ext 目录下。这样 JMeter 就能在运行时找到 AESUtil 类了。
⑤ 在 JSR223 前/后置处理器中使用 Java 调用加解密方法
在 JMeter 中添加 JSR223 后置处理器,将语言选择为 Java,注意在开头一定要先导入加解密工具类“import
com.csb.meeting.crypto.utils.AESUtil;”,例如解密会议列表:
import com.meeting.crypto.utils.AESUtil;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
try {
// 获取 JMeter 变量
JMeterVariables vars = JMeterContextService.getContext().getVariables();
// 从 JMeter 变量中获取加密文本和 AES 密钥
String meetingList = vars.get("meetingList");
String aesKey = vars.get("aesKey");
// 检查变量是否为空
if (encryptedText == null || aesKey == null) {
System.err.println("加密文本或 AES 密钥为空,请检查用户定义的变量。");
return;
}
// 调用 decryptAes 方法进行解密
String decryptedText = AESUtil.decryptAes(meetingList, aesKey);
// 将解密后的结果保存到 JMeter 变量中
vars.put("decryptedText", decryptedText);
// 打印日志,方便调试
System.out.println("AES 密钥: " + aesKey);
System.out.println("解密后的数据: " + decryptedText);
}
catch (Exception e) {
log.error("Decryption failed: " + e.getMessage(), e);
}
查看控制台,成功解密数据并打印:
2.Jmeter脚本设计
① 添加用户自定义的变量
将一些不经常变化的公共参数抽离到用户自定义的变量中保存,后续调用时直接使用${variableName}进行调用即可。
② 添加HTTP公共请求头
将一些每个接口中都会带的请求头抽离到公共请求头中保存,这样每个接口在发起请求的时候都会带上这些请求头内容。
如果个别接口需要传递一些单独的请求头内容,再在该接口下添加一个单独的请求头即可。例如创建会议接口需要带上“ec”和“ev”:
③ 添加HTTP请求
添加HTTP请求,填写接口路径,参数等等。
④ 添加监听器
- 添加查看结果树:可以查看到每个请求的运行结果、请求参数、响应参数等信息,便于定位问题;
- 添加聚合报告监听器,用于查看每个接口的性能指标,如平均响应时间、吞吐量、错误率等;
- 添加图形结果监听器,以图形化方式展示接口的响应时间和吞吐量变化。
⑤ 添加前置处理器,加密参数
例如:创建会议接口需要先组装参数,再调用加密方法对参数进行加密。此时就需要在HTTP请求下添加“JSR223前置处理器”,语言选择Java,然后编写相应代码。
代码解释:
- 导入相应的包,尤其是自定义的加解密工具类;
- 从Jmeter全局变量中读取密钥和其他相应参数,用于加密操作;
- 引用系统自带的函数,生成自定义参数,用于接口参数组装;
- 调用工具类中的加密方法,对组装好的参数进行加密;
- 将加密后的密文保存到Jmeter全局变量中,HTTP请求中引用组装后的密文;
⑥ 添加断言
断言有多种类型,我只断言了接口返回值中的code字段是否等于200,基本上返回200就表示接口响应成功了(并不是HTTP响应状态码)。
⑦ 添加JSON提取器,提取返回值
由于接口返回的内容也是加密的,所以需要先把密文部分全部提取出来,后续再用后置处理器进行解密,提取指定字段的返回值。
⑧ 添加后置处理器,解密密文并保存
与前置处理器类似,需要在HTTP请求下添加“JSR223后置处理器”,语言选择Java,然后编写相应代码。
代码解释:
- 导入相应的包,尤其是自定义的加解密工具类;
- 从Jmeter全局变量中读取密钥和其他相应参数,例如前面保存的meetingInfo;
- 调用工具类中的解密方法,对meetingInfo进行解密;
- 再从解密后的内容中提取指定字段返回值,例如meetingNumber,
- 将meetingNumber保存到Jmeter全局变量中,后续的接口再添加前置处理器,从Jmeter全局变量中读取这个meetingNumber;
⑨ 添加循环控制器,模拟多个状态上报
其中会控接口和状态上报接口的参数,是个枚举值数组,不同的值代表不同动作,所以这里添加一个循环控制器,将会控接口和状态上报接口放进去,循环三次,每次都是取随机值,这样就可以模拟三种不同的状态上报和会控操作。
⑩ 添加随机定时器
添加随机定时器,模拟接口之间的延时,时间设置为100ms~150ms,类似于人手动点击某个页面(发起A请求)后,又点了某个按钮(发起B请求),二者之间的思考间隔。
3.验证Jmeter脚本
上述所有工作完成后,就可以运行脚本进行调试验证了。其实无论是写代码还是写Jmeter脚本,无论是小白还是大神,尤其是实现比较复杂的情况下,基本没有一次就运行通过的。在经历了无数次的“报错->排查定位->修改->再运行”后,看到全绿,就表示大功告成了!
至此,终于完成了脚本开发的阶段性目标。
相关推荐
- 当Frida来“敲”门(frida是什么)
-
0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...
- 服务端性能测试实战3-性能测试脚本开发
-
前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...
- Springboot整合Apache Ftpserver拓展功能及业务讲解(三)
-
今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...
- Linux和Windows下:Python Crypto模块安装方式区别
-
一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...
- Python 3 加密简介(python des加密解密)
-
Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...
- 怎样从零开始编译一个魔兽世界开源服务端Windows
-
第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...
- 附1-Conda部署安装及基本使用(conda安装教程)
-
Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...
- 如何配置全世界最小的 MySQL 服务器
-
配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...
- 如何使用Github Action来自动化编译PolarDB-PG数据库
-
随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...
- 面向NDK开发者的Android 7.0变更(ndk android.mk)
-
订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...
- 信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要
-
问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...
- OpenSSH 安全漏洞,修补操作一手掌握
-
1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...
- Linux:lsof命令详解(linux lsof命令详解)
-
介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...
- 幻隐说固态第一期:固态硬盘接口类别
-
前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...
- 新品轰炸 影驰SSD多款产品登Computex
-
分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...
- 一周热门
- 最近发表
-
- 当Frida来“敲”门(frida是什么)
- 服务端性能测试实战3-性能测试脚本开发
- Springboot整合Apache Ftpserver拓展功能及业务讲解(三)
- Linux和Windows下:Python Crypto模块安装方式区别
- Python 3 加密简介(python des加密解密)
- 怎样从零开始编译一个魔兽世界开源服务端Windows
- 附1-Conda部署安装及基本使用(conda安装教程)
- 如何配置全世界最小的 MySQL 服务器
- 如何使用Github Action来自动化编译PolarDB-PG数据库
- 面向NDK开发者的Android 7.0变更(ndk android.mk)
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)