JMeter安装及性能测试方案

文章详细介绍了JMeter的安装过程,包括JDK的安装和JMeter的配置。接着,它提供了测试方案的示例,包括测试模板的XML结构和解释。此外,还讨论了JMeter的分布式集群配置,强调了在同一内网环境下,如何配置多台压测服务器和控制服务器进行分布式测试。最后,文章提到了一些参数配置的说明,如线程数、Ramp-Up时间和持续时间,并解答了可能出现的错误,如文件打开限制和内存问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. JMeter的安装

  安装JDK环境:

rpm -ivh jdk-8u171-linux-x64.rpm
java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

  安装 jmeter:官方下载地址

tar xvf jmeter.tar.gz 
mv apache-jmeter-5.4.1/ jmeter

vim /etc/profile
export JMETER_HOME=/root/jmeter
export CLASSPATH=$JMETER_HOME/lib/ext/ApacheJMeter_core.jar:$JMETER_HOME/lib/jorphan.jar:$CLASSPATH
export PATH=$JMETER_HOME/bin:$PATH:$HOME/bin

source /etc/profile
jmeter -v
Jan 30, 2023 11:47:41 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
    _    ____   _    ____ _   _ _____       _ __  __ _____ _____ _____ ____
   / \  |  _ \ / \  / ___| | | | ____|     | |  \/  | ____|_   _| ____|  _ \
  / _ \ | |_) / _ \| |   | |_| |  _|    _  | | |\/| |  _|   | | |  _| | |_) |
 / ___ \|  __/ ___ \ |___|  _  | |___  | |_| | |  | | |___  | | | |___|  _ <
/_/   \_\_| /_/   \_\____|_| |_|_____|  \___/|_|  |_|_____| |_| |_____|_| \_\ 5.4.1

Copyright (c) 1999-2021 The Apache Software Foundation

2. JMeter测试方案

  测试模板(search.jmx)请在本机Window下运行jmeter GUI来生成,保存后再上传到测试服务器上。

在这里插入图片描述
 
  下面是根据模板自己修改后的一个测试模板文件:

<?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.serialize_threadgroups">true</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="服务器变量" enabled="true">
        <collectionProp name="Arguments.arguments">
          <elementProp name="threadNum" elementType="Argument">
            <stringProp name="Argument.name">threadNum</stringProp>
            <stringProp name="Argument.value">${__P(threadnum,)}</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="time" elementType="Argument">
            <stringProp name="Argument.name">time</stringProp>
            <stringProp name="Argument.value">${__P(time,)}</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="links" elementType="Argument">
            <stringProp name="Argument.name">links</stringProp>
            <stringProp name="Argument.value">172.17.15.31</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="port" elementType="Argument">
            <stringProp name="Argument.name">port</stringProp>
            <stringProp name="Argument.value">8888</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="http" elementType="Argument">
            <stringProp name="Argument.name">http</stringProp>
            <stringProp name="Argument.value">http</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
        </collectionProp>
      </Arguments>
      <hashTree/>
      <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="调试变量" enabled="false">
        <collectionProp name="Arguments.arguments">
          <elementProp name="threadNum" elementType="Argument">
            <stringProp name="Argument.name">threadNum</stringProp>
            <stringProp name="Argument.value">5000</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="time" elementType="Argument">
            <stringProp name="Argument.name">time</stringProp>
            <stringProp name="Argument.value">300</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="links" elementType="Argument">
            <stringProp name="Argument.name">links</stringProp>
            <stringProp name="Argument.value">172.17.15.31</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="port" elementType="Argument">
            <stringProp name="Argument.name">port</stringProp>
            <stringProp name="Argument.value">8888</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
          <elementProp name="http" elementType="Argument">
            <stringProp name="Argument.name">http</stringProp>
            <stringProp name="Argument.value">http</stringProp>
            <stringProp name="Argument.metadata">=</stringProp>
          </elementProp>
        </collectionProp>
      </Arguments>
      <hashTree/>
      <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="false">
        <boolProp name="ResultCollector.error_logging">true</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/>
      <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/>
      <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">${threadNum}</stringProp>
        <stringProp name="ThreadGroup.ramp_time">10</stringProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
        <stringProp name="ThreadGroup.duration">${time}</stringProp>
        <stringProp name="ThreadGroup.delay">1</stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">false</boolProp>
      </ThreadGroup>
      <hashTree>
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="false">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Content-Type</stringProp>
              <stringProp name="Header.value">application/json</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
        <hashTree/>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="search" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${links}</stringProp>
          <stringProp name="HTTPSampler.port">${port}</stringProp>
          <stringProp name="HTTPSampler.protocol">${http}</stringProp>
          <stringProp name="HTTPSampler.contentEncoding">utf-8</stringProp>
          <stringProp name="HTTPSampler.path">/c/suggestion?appKey=dcf09be0f8993e896ba4de940f1692c3&amp;channelCode=50000138&amp;keyword=${__RandomString(5,abcdefghijklmnopqrstuvwxyz,)}</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.implementation">Java</stringProp>
          <stringProp name="HTTPSampler.connect_timeout">30000</stringProp>
          <stringProp name="HTTPSampler.response_timeout">30000</stringProp>
          <stringProp name="TestPlan.comments">http://172.xxx.xxx.31:8888/c/suggestion?appKey=dcf09be0f8993e8960f1692c3&amp;channelCode=5012345&amp;keyword=ab</stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="false">
            <collectionProp name="Asserion.test_strings"/>
            <stringProp name="Assertion.custom_message"></stringProp>
            <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
            <boolProp name="Assertion.assume_success">false</boolProp>
            <intProp name="Assertion.test_type">2</intProp>
          </ResponseAssertion>
          <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/>
        </hashTree>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>
mkdir /root/jmeter/log
mkdir /root/jmeter/script
ls /root/jmeter/script/
search.jmx
cd /root/jmeter
vim start.sh
jmeter -J threadnum=$1 -J time=$2 -n -t ./script/search.jmx -l ./log/log$1-$2-$3.txt -e -o ./log/report$1-$2-$3
chmod +x start.sh

# -n:以非GUI形式运行Jmeter 
# -t:运行JMX测试计划脚本文件的路径,./script/search.jmx
# -l:运行结果保存路径(.jtl),此文件必须不存在
# -j: jmeter运行日志
# -r: 运行分布式压测服务器,指明用jmeter属性"remote_hosts"
# -R:运行分布式服务器,其后跟着服务器列表
# -e:在脚本运行结束后生成html报告,此参数要与-l一起使用。 
# -o:用于存放html报告的目录,此目录必须为空。此参数与-e一起使用。

./start.sh 5000 300 1

3. JMeter分布式集群配置

  由于单台压测服务器并不足以进行创建过多线程时,则可以使用分布式测试。将所有测试线程都由各台压测服务器同时产生,并将测试结果返回到控制服务器。控制服务器与压测服务器可以是同一台服器。

要求:

  • 压测服务器、控制服务器、被测试服务器要在同一内网;
  • 压测服务器、控制服务器安装的java版本一致;
  • 压测服务器、控制服务器运行的jmeter版本一致;

压测服务器、控制服务器的jmeter的SSL设置:

  所有压测服务器与控制服务器的通讯,默认以ssl证书通讯。可以在控制服务器中使用./create-rmi-keystore.sh 命令(jmeter软件bin目录下中自带)产生.jks后缀名的证书,将此证书存放到各压测服务器的jmeter软件的bin目录下。
  个人不太建议使用SSL证书通讯方式,本人实际测试时,在1099端口开放且并未占用的正常情况下,压测服务器并未收到控制服务器的测试指令的问题,用telnet测试端口可以正常远程连接。所以开启SSL不靠谱。
  所以建议在jmeter.properties配置文件中,关闭SSL选项:

server.rmi.ssl.disable=true

  注意每台压测服务器和控制服务器都要修改此选项,这样就不需要SSL证书了。
  在每台压测服务器上运行压测服务:

./jmeter-server -Djava.rmi.server.hostname=内网IP(被测服务器IP

  在控制服务器上,设置压测服务器IP,打开jmeter.properties文件,修改remote_hosts配置值:

vim ./bin/jmeter.properties
# remote_hosts=127.0.0.1 注销此条
remote_hosts=192.168.24.11:1099,192.168.24.12:1099

  多台压测服务器之间用逗号相隔。1099是默认通讯端口,可省略写入。
  如果压测服务器的侦测端口1099被修改了,那么的remote_hosts的IP地址后面必须写上端口

#server_port=1099

  测试端口是否被占用:

 ss -tnlp | grep 1099

  一切准备就绪。在控制器启动测试:

./jmeter -n -t ./Test_Plan.jmx -r -l ./result.jtl -e -o ./html

  Test_Plan.jmx是测试计划文件, result.jtl是测试结果文件。

4. JMeter参数配置说明

在这里插入图片描述
  线程数:指测试时要发起压测的线程数量,相当于模拟访问用户数。如果是分布式压力测试,则每台压测服务器都会按这个数量产生线程,而不是整个测试的总线程数。
  Ramp-Up时间:所有线程创建完成所需的时间,以上方30秒设置为例,则30秒时间产生10000个线程,每秒则产生10000/30 = 约 999 条线程;
  循环次数:每个线程循环执行的次数,勾选“永远”则表示不限次数地循环执行,一直到下方的“持续时间”结束为止,如果所有线程循环完预定的次数,则当前测试就会停止了,就算下方的“持续时间”到了实际测试也已经是完成了。
  持续时间(秒):当勾选“调度器”时,表示整个测试要持续执行的时间。实际测试时并非完全遵循此时间结束,如果上方的“循环次数”设置一个数值时,就算已经在测试时已经过了设定的持续时间,如果线程未循环完预定的循环次数,仍然会循环执行完为止。
  启动延迟(秒):测试开始时需延迟多少秒才开始。
“same user on each iteration":表示每次循环是否使用同一用户(即同一线程)执行,这对于web测试是否需要保持同一会话时有用;
  ”延迟创建线程直到需要“:如选项如果勾选,则在Ramp-Up时间结束后等所有线程都启动完毕才开始执行测试,如果Ramp-Up时间未结束时,已创建的线程则在等待状态,并未开始执行测试。实际测试中,此选项几乎不会影响测试结果 。

5. FAQ

  • 如果遇到 open too many files,说明遇到打开文件最大值问题。可进行如下设置:
echo "ulimit -HSn 65536" >> /etc/rc.local
echo "ulimit -HSn 65536" >> /root/.bash_profile
ulimit -HSn 65536
  • 如果遇到out of memory异常,则增大JVM内存池。编辑jmeter文件,找到:
vim bin/jmeter

"${HEAP:="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m"}"
"${HEAP:="-Xms4096m -Xmx4096m -XX:MaxMetaspaceSize=256m"}"
  • 检查TCP/IP连接状态和对应个数:
netstat -an | awk '/^tcp/ {++s[$NF]} END {for(a in s) print a, s[a]}'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

降世神童

学都学了,看也看了,感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值