介绍
本文将介绍如何使用Apache JMeter(JMeter)工具对Webman框架进行极限压测,以评估其性能。JMeter是一个广泛使用的开源负载测试工具,可用于模拟高并发请求并评估系统的响应能力。通过使用JMeter进行极限压测,我们可以深入了解Webman框架在高负载条件下的性能表现。
ECS 配置
代码语言:javascript复制2 vCPU 8 GiB (I/O优化)
ecs.g7.large 100Mbps (峰值)
压测场景
Windows 环境
在Windows环境下测试,并发数过大,整个主机直接崩掉.
Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz 2.81 GHz
64 位操作系统 24.0 GB
Linux 环境
依赖说明
在Linux系统中运行JMeter脚本,需要有两个前提:
- Linux系统中配置好Java环境
- Linux系统中配置好JMeter环境。(因为JMeter的运行需要Java环境)
环境说明
Java版本
代码语言:javascript复制java -version
openjdk version "17.0.3" 2022-04-19
OpenJDK Runtime Environment (build 17.0.3 7-Ubuntu-0ubuntu0.20.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3 7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)
Linux操作系统
代码语言:javascript复制Ubuntu 20.04.4 LTS
安装JMeter
「下载JMeter」
下载地址:http://jmeter.apache.org/download_jmeter.cgi
压测
编写JMeter脚本
「tinywan.jmx
脚本」
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">28000</stringProp>
<stringProp name="ThreadGroup.ramp_time">10</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">60</stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="临界部分控制器" enabled="false">
<stringProp name="CriticalSectionController.lockName">global_lock</stringProp>
</CriticalSectionController>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="1-频道详情" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="user_id" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${__Random(1000,8000,user_id)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">user_id</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">172.30.237.66</stringProp>
<stringProp name="HTTPSampler.port">8788</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/channel/watch/20220023/detail</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="2-白名单验证" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="user_id" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${__Random(1000,8000,user_id)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">user_id</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">172.30.237.66</stringProp>
<stringProp name="HTTPSampler.port">8788</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/channel/watch/20220023/validate-whitelist</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="3-频道详情" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="user_id" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${__Random(1000,8000,user_id)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">user_id</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">172.30.237.66</stringProp>
<stringProp name="HTTPSampler.port">8788</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/channel/watch/20220023/detail</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree/>
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="聚合报告" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
脚本关键参数说明
- -n:表示non gui mode,就是非图形化模式。
- -t:指定JMX脚本路径,即test plan(测试计划)。后面跟需要运行的JMeter 脚本的路径和脚本名称。
- -l:指定结果文件路径。后面跟输出结果文件路径和结果文件名称。-e:设置测试完成后生成测试报表。-e:设置测试完成后生成测试报表。-m:设置测试完成后生成测试报表。
- 1)若没有指定指定结果文件路径,则自动创建,可以生成.csv文件或者.jtl文件。
- 2)若只写脚本名称,则默认是在当前目录查找或创建。
- -e:设置测试完成后生成测试报表。
- -o:指定测试报表生成文件夹。文件夹必须为空或不存在。
压测命令
代码语言:javascript复制/root/apache-jmeter-5.4.1/bin/jmeter -n -t /root/tinywan.jmx -l tinywan-result.jtl
正常压测
代码语言:javascript复制Creating summariser <summary>
Created the tree successfully using /root/33.jmx
Starting standalone test @ Sat May 07 16:31:56 CST 2022 (1651912316484)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary 18271 in 00:00:03 = 5247.3/s Avg: 123 Min: 3 Max: 3383 Err: 0 (0.00%) Active: 3471 Started: 3471 Finished: 0
summary 176369 in 00:00:30 = 5879.0/s Avg: 442 Min: 7 Max: 31083 Err: 0 (0.00%) Active: 5000 Started: 5000 Finished: 0
summary = 194640 in 00:00:33 = 5813.3/s Avg: 412 Min: 3 Max: 31083 Err: 0 (0.00%)
summary 162025 in 00:00:28 = 5703.1/s Avg: 1316 Min: 3 Max: 58295 Err: 0 (0.00%) Active: 0 Started: 5000 Finished: 5000
summary = 356665 in 00:01:02 = 5762.7/s Avg: 822 Min: 3 Max: 58295 Err: 0 (0.00%)
Tidying up ... @ Sat May 07 16:32:58 CST 2022 (1651912378410)
... end of run
并发10000压测
代码语言:javascript复制「并发数超过
10000
,提示Java虚拟机内存异常」
Creating summariser <summary>
Created the tree successfully using /root/33.jmx
Starting standalone test @ Sat May 07 16:35:05 CST 2022 (1651912505615)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary 1 in 00:00:00 = 5.6/s Avg: 76 Min: 76 Max: 76 Err: 0 (0.00%) Active: 189 Started: 189 Finished: 0
summary 57074 in 00:00:24 = 2357.4/s Avg: 1096 Min: 2 Max: 11532 Err: 0 (0.00%) Active: 14671 Started: 14671 Finished: 0
summary = 57075 in 00:00:24 = 2340.1/s Avg: 1096 Min: 2 Max: 11532 Err: 0 (0.00%)
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid152251.hprof ...
Heap dump file created [1399681687 bytes in 2.987 secs]
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "线程组 1-14668"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-9350"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-14684"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-7834"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-8421"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-8792"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-7965"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-14692"
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-8261"
^C^C
Exception: java.lang.NoClassDefFoundError thrown from the UncaughtExceptionHandler in thread "线程组 1-9190"
翻阅官方文档 https://jmeter.apache.org/usermanual/get-started.html
启动 JMeter 时使用的 JVM 内存设置。默认值
代码语言:javascript复制-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m
通过设置变量 JVM_ARGS 将覆盖默认预定义的设置
代码语言:javascript复制JVM_ARGS="-Xms1024m -Xmx1024m" jmeter -t test.jmx [etc.]
重新改写命令,设置内存为4G
代码语言:javascript复制JVM_ARGS="-Xms4024m -Xmx4024m" /root/apache-jmeter-5.4.1/bin/jmeter -n -t /root/tinywan.jmx -l tinywan-result.jtl
再次运行结果(异常问题已解决)
代码语言:javascript复制# JVM_ARGS="-Xms4024m -Xmx4024m" /root/apache-jmeter-5.4.1/bin/jmeter -n -t /root/33.jmx -l 33-result.jtl
Creating summariser <summary>
Created the tree successfully using /root/33.jmx
Starting standalone test @ Sat May 07 16:51:19 CST 2022 (1651913479599)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary 52703 in 00:00:10 = 5084.7/s Avg: 228 Min: 3 Max: 9561 Err: 0 (0.00%) Active: 25654 Started: 25654 Finished: 0
summary 169987 in 00:00:30 = 5666.2/s Avg: 549 Min: 6 Max: 39292 Err: 0 (0.00%) Active: 28000 Started: 28000 Finished: 0
summary = 222690 in 00:00:40 = 5516.9/s Avg: 473 Min: 3 Max: 39292 Err: 0 (0.00%)
summary 165345 in 00:00:30 = 5451.9/s Avg: 9050 Min: 3 Max: 61988 Err: 0 (0.00%) Active: 3626 Started: 28000 Finished: 24374
summary = 388035 in 00:01:11 = 5489.0/s Avg: 4127 Min: 3 Max: 61988 Err: 0 (0.00%)
summary 8713 in 00:00:01 = 7543.7/s Avg: 10948 Min: 3 Max: 60595 Err: 0 (0.00%) Active: 0 Started: 28000 Finished: 28000
summary = 396748 in 00:01:12 = 5522.0/s Avg: 4277 Min: 3 Max: 61988 Err: 0 (0.00%)
Tidying up ... @ Sat May 07 16:52:31 CST 2022 (1651913551484)
... end of run
总结
负载均衡SLB测试
「单机QPS:3000左右」
单机测试
移除负载均衡SLB(由于https证书太垃圾了,导致证书验证老是超时问题)
「QPS最大是:28000」(server数:16个)
代码语言:javascript复制JVM_ARGS="-Xms4024m -Xmx4024m" /root/apache-jmeter-5.4.1/bin/jmeter -n -t /root/tinywan.jmx -l tinywan-result.jtl
Creating summariser <summary>
Created the tree successfully using /root/33.jmx
Starting standalone test @ Sat May 07 16:51:19 CST 2022 (1651913479599)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary 52703 in 00:00:10 = 5084.7/s Avg: 228 Min: 3 Max: 9561 Err: 0 (0.00%) Active: 25654 Started: 25654 Finished: 0
summary 169987 in 00:00:30 = 5666.2/s Avg: 549 Min: 6 Max: 39292 Err: 0 (0.00%) Active: 28000 Started: 28000 Finished: 0
summary = 222690 in 00:00:40 = 5516.9/s Avg: 473 Min: 3 Max: 39292 Err: 0 (0.00%)
summary 165345 in 00:00:30 = 5451.9/s Avg: 9050 Min: 3 Max: 61988 Err: 0 (0.00%) Active: 3626 Started: 28000 Finished: 24374
summary = 388035 in 00:01:11 = 5489.0/s Avg: 4127 Min: 3 Max: 61988 Err: 0 (0.00%)
summary 8713 in 00:00:01 = 7543.7/s Avg: 10948 Min: 3 Max: 60595 Err: 0 (0.00%) Active: 0 Started: 28000 Finished: 28000
summary = 396748 in 00:01:12 = 5522.0/s Avg: 4277 Min: 3 Max: 61988 Err: 0 (0.00%)
Tidying up ... @ Sat May 07 16:52:31 CST 2022 (1651913551484)
... end of run