前言
近期即将上线一个在线考试类的系统,由于甲方客户比较重视此次考试,所以各种准备工作也要做足。故此对线上系统做了一次比较全面的压力测试,也是通过这次测试,验证了之前的很多想法,自感收获颇丰,故留此文。
准备测试用例
正常来说,待测用例可能是接口,也可能是页面,或者是一连串的操作动作,比如先登录,再浏览某页面,再提交某表单等等。具体情况不同,准备用例的复杂程度也有区分。我这里就是准备了几个接口,然后把接口里实现了一些模拟真实情况的操作,这样准备的用例就会很简单。
概括一下就是,数据库读,缓存读,数据库写,数据库更新 4 个操作
具体代码就不贴了,都是些业务操作,只简单贴一下接口名和一些接口配置
#region 性能压力测试相关接口,模拟考试各种动作
/// <summary>
/// 模拟获取考试列表,并缓存
/// </summary>
/// <param name="associationId"></param>
/// <returns></returns>
[AllowAnonymous]
[ResponseCache(Duration = 100)]
public async Task<IActionResult> TestGetExaminations(string token)
{
//业务动作
}
/// <summary>
/// 模拟验证身份接口
/// 这里只验证接口访问情况,故将所有情况返回值都设定为code=1
/// </summary>
/// <returns></returns>
[AllowAnonymous]
public async Task<IActionResult> TestInfoVerification(string token,string testExamId="",string testIdNumber="")
{
//业务动作
}
/// <summary>
/// 模拟抽卷,
/// 这里不实际抽题,执行一个相对的耗时插入操作,并标记为删除
/// </summary>
/// <returns></returns>
[AllowAnonymous,HttpPost]
public async Task<IActionResult> TestConfirmMyPaper(string token)
{
//业务动作
}
/// <summary>
/// 模拟提交答题
/// </summary>
/// <returns></returns>
[AllowAnonymous, HttpPost]
public async Task<IActionResult> TestSubmitPaper(string token)
{
//业务动作
}
#endregion
注意,我把这几个接口的访问级别设定成了不需要验证即可访问,一是为了方便,二是由于我们的用户中心是隔离出去的,也是为了避免多个系统的影响,直接测试本系统性能。
搭建 JMeter 分布式压测环境
Why?
JMeter 不用多介绍,官方文档在这里👉:https://jmeter.apache.org/,目前最新版本是 5.5 之所以要搭建分布式的压测环境,主要还是想最大限度的模拟真实情况,因为 JMeter 是用 Java 语言编写的,对资源的消耗不算低,单机能模拟的最高 UVs 是有上限的,设定的值太高软件自身就会崩溃。为了解决这个问题,JMeter 官方提供了分布式部署的解决方案。
准备依赖环境
JMeter 必备的依赖环境就是 JDK,但由于 JDK1.8 以后的版本商用会有一定的风险,所以我这里使用的版本是微软构建的 OpenJDK 17 这里呢,微软也为用户提供了详细的安装手册,包含市面上几乎所有的主流系统,地址在这里👉:https://learn.microsoft.com/zh-cn/java/openjdk/install我这里是 CentOS 环境和 Windows 环境,安装好后的截图如下
注意,所有终端机器的 jdk 版本要保持一致!
下载 JMeter
到官方网站下载最新版本的 JMeter,目前是 5.5,地址👉:https://jmeter.apache.org/download_jmeter.cgi具体下载过程不再细说,centos 可用 wget,也可以在 windows 下载好后通过 GUI 工具上传到 Linux 环境
配置压力机
我这里是把 2 台 CentOS 机器配置成了压力机(Slave),配置过程如下进入 bin 目录,打开 jmeter.properties 文件分别查找并修改以下几个参数
-
language:放开注释并设置成 zh_CN,这个不是必须的,这样操作后,用到 GUI 界面的时候会显示中文。
-
remote_hosts:修改为当前压力机 ip 地址+端口,端口默认是 1099,可以改成任意未被占用的端口
-
server_port:跟随上面的端口设置,注释或者不设置则未 1099
-
server.rmi.localport:跟随上面端口设置,这个需要放开注释
-
server.rmi.ssl.disable:为了方便设置成 true 即可,
若考虑安全因素,可到 bin 目录下找到‘create-rmi-keystore.bat’或者‘create-rmi-keystore.sh’文件,以 bat 文件为例,拷贝该文件到 jdk 的 bin 目录下,执行该文件,根据控制台提示,输入完成后会得到一个 rmi_keystore.jks 文件,把这个文件拷贝到所有压力机和控制机上。
我这里为了方便,就直接把 server.rmi.ssl.disable 设置成了 true。最后我这里的压力机主要配置如下,2 台配置除了 ip 都一样
#...其他配置
remote_hosts=10.185.1.178:1029
server_port=1029
server.rmi.localport=1029
server.rmi.ssl.disable=true
#...其他配置
配置完成后,我这里的压力机还要对 jmeter-server 文件修改下执行权限
chmod 777 jmeter-server
然后修改 jmeter-server 文件放开 RMI_HOST_DEF 参数的注释,并修改 hostname 为本机 ip(所有 slave 压力机都要修改)
DIRNAME=`dirname $0`
# If the client fails with:
# ERROR - jmeter.engine.ClientJMeterEngine: java.rmi.ConnectException: Connection refused to host: 127.0.0.1
# then it may be due to the server host returning 127.0.0.1 as its address
# One way to fix this is to define RMI_HOST_DEF below
RMI_HOST_DEF=-Djava.rmi.server.hostname=10.185.1.176
${DIRNAME}/jmeter ${RMI_HOST_DEF} -Dserver_port=${SERVER_PORT:-1099} -s -j jmeter-server.log "$@"
到这里,就可以执行压力机上的服务节点了
注意:由于数据传输问题,slave 机和 master 机的传输,需要把防火墙关闭掉,这个算是个坑把,开始我以为只放开端口就可以了,但后来数据回传一直有问题,就直接把整个防火墙关掉了,然后问题就解决了。
这个不知道是不是 JMeter 的问题,还是说有其他的解决方案。但不管怎么说关闭防火墙是一个非常危险的操作,非内网安全环境下,谨慎操作!
关闭命令如下
systemctl stop firewalld.service
解决到数据传输的问题后,进入 bin 目录启动服务
./jmeter-server
出现如下界面即为成功。
这里,还要注意,为了尽可能提高单台压力机的压测性能上限,可以把默认的 jvm 内存限制调高,这里根据官方文档的建议,增加 setenv.sh 文件(Windows 是 sentenv.bat)写入下面代码即可
export JVM_ARGS="-Xms1G -Xmx1G -XX:MaxMetaspaceSize=192m"
# 默认是 -Xms256m -Xmx256m -XX:MaxMetaspaceSize=256m
这里具体的解释可以参照官网介绍,进入该地址:https://jmeter.apache.org/usermanual/get-started.html,搜索 jvm 关键字即可
配置控制机
控制机建议在一台熟悉且方便的系统环境上配置,我这里用的 Windows,配置内容和压力机略有不同进入 bin 目录,打开 jmeter.properties 文件
-
remote_hosts:配置为所有压力机的 ip+端口
-
server.rmi.ssl.disable:配置为 true,注意事项跟压力机配置部分描述的内容相同
因为控制机不承担压测任务,只配置好这些参数,其他保持默认就好
控制机也可以作为压力机和 slave 节点一起工作,配置上就放开端口,并把当前 ip 配置到 remote_hosts 上即可
准备压测配置文件
这里有多种方式配置,可以在控制机上启动 GUI,设置好接口,监视器等,然后保存为 jmx 文件;也可以依托一些第三方的工具,比如 ApiFox,配置好测试场景后,就可以在性能测试窗口,直接导出配置文件。
JMeter GUI 配置界面如下
ApiFox 的配置界面如下
这部分也不多介绍了,最后生成的文件就是一个 xml 风格的 jmx 文件~长这样👇
开始压力测试
上面的流程配置完成后,执行压力测试就非常简单了,我这里写了一个 bat 文件,供参考我这里在 jmeter 根目录下新建了个 result 文件夹,然后把 jmx 测试文件和下面这个可执行文件放到一起
@echo off
cd /d %~dp0
call ..\bin\jmeter.bat -n -t exam.jmx -r -l testResult%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%.jtl -e -o ResultReport_%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%
echo %date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2% jmeter ok! >> log.txt
执行 bat 文件后,正常情况下可以在控制机上看到下面信息
对应的压力机上
根据我们设定的压测参数,包括持续时长,线程数(UVs),ramp-up 等,待程序执行完成后,会在我们设定的地址生成(在上面的 bat 文件里有配置)一个报告目录,然后点击 index.html,就可以查看压测的数据报告了
至此,分布式压测的环境搭建和执行环节就结束了。
后续我们就可以动态的调整参数,进行不同参数条件下的压力测试了。
比如,单机负载下, 最大可以有多大并发,双机,多机呢?
再比如,对照不同部署环境,比如 apache,iis,kestrel,nginx
这里多说一句,我这里实测了 iis 和 kestrel 两种情况,iis 真的是渣!
再比如,对照增加缓存和不增加缓存的情况
再比如,数据库单机,数据库集群的情况等等
得到了每次测试的图表数据之后,就对各个项目进行分析,比如响应时长(RT),吞吐量(QPS),每秒接收流量,发送流量,样本量等各项数据指标,就像看体检报告单。
通过一轮轮的测试,可以慢慢找到我们系统的性能瓶颈,除了帮助我们优化系统,优化硬件配置,还可以额给到用户一个可靠的,客观的并发承载规模,避免扯皮。
总而言之,压力测试在一些直接 ToC,或者用户规模相对较高的却又不好估量的场景,是非常必要的一个环节!
附加推荐
最后,想在介绍几款SaaS 类的压测工具。
当我们手头上没有压力机资源,或者准备资源不是很方便的时候,就可以考虑一些第三方的SaaS类工具。比如阿里云,腾讯云这些大厂,他们都提供了压测类的性能测试工具,而且有具体的操作文档(我在自己搞压测的时候,就经常看他们的文档,一是看看他们的工具怎么用,更主要的是学习压测类的知识,毕竟咱也不是专业测试~哈哈)。
我个人用过腾讯云的云压测产品,整体来说还是非常好用的,关键它支持直接导入jmx配置文件,更加方便。图表什么的也比jmeter自己生成的要丰富,而且新用户赠送2万UVs额度,推荐大家试一试,传送门👉:腾讯云运营活动 - 腾讯云
除了腾讯云的云压测,上面提到的APIFox也提供压力测试能力,界面非常酷。
还有一个国产做的非常不错的压测工具,RunnerGo,他的底层是用go语言实现的,我试了一下也非常不错,可以自定义各种测试场景,增加了一些控制机制,更加贴近真实,也推荐大家去尝试一下。
同步转发于infoq:高并发高并发,多高算高?这个值是怎么来的?_Jmeter_为自己带盐_InfoQ写作社区