1. 程式人生 > >Apache JMeter壓力測試快速入門

Apache JMeter壓力測試快速入門

引言

前文《Apache JMeter功能測試快速入門》中,我們在bin目錄下點選jmeter.bat啟動JMeter時,目光如炬的你一定注意到了命令列視窗中的如下提示:
Don't use GUI mode for load testing, only for Test creation and Test debugging !
For load testing, use NON GUI Mode:
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to output folder]
& adapt Java Heap to your test requirements:
   Modify HEAP="-Xms512m -Xmx512m" in the JMeter batch file

這段提示文字直譯過來就是:
不要使用GUI模式進行負載測試,僅用於測試建立和測試除錯!
對於負載測試,使用NON GUI模式:
jmeter -n -t [jmx檔案] -l [結果檔案] -e -o [輸出資料夾的路徑]
並根據您的測試需求調整Java堆:
在JMeter批處理檔案中修改HEAP="-Xms512m -Xmx512m"

以此為指導思想,本回要講述的主要內容便是JMeter壓力測試的幾個環節:
(1) 建立用於效能測試的測試計劃
(2) 在GUI模式下執行測試計劃
(3) 在NON GUI模式(命令列模式)執行測試計劃

開始壓力測試前,我們先來了解下相關的術語。

術語定義

併發使用者數

併發使用者數,指系統中同時進行業務操作的使用者數,在效能測試工具中,一般通過開啟多執行緒模擬虛擬使用者。

TPS

TPS(Transaction Per Second), 即每秒事務數, 是衡量系統性能的一個非常重要的指標。

APDEX

APDEX (Application Performance Index),即效能指數,是使用者對應用效能滿意度的量化值。它提供了一個統一的測量和報告使用者體驗的方法,把終端使用者的體驗和應用效能作為一個完整的指標進行統一度量。
基於“響應性”,APDEX定義了3個使用者滿意度區間( OneAPM預設定義的T值為0.5秒):
S(滿意 ):這樣的響應時間讓使用者感到很愉快,響應時間少於T
T(容忍 ):慢了一點,但還可以接受,繼續這一應用過程,響應時間T~4T(JMeter中為3T)
F(失望 ):太慢了,受不了了,使用者決定放棄這個應用,響應時間超過4T(JMeter中為3T)

測試說明

測試環境:Win 10/Ubuntu 16.04 + JDK 1.8 +Apache JMeter 3.2
測試指令碼:下載測試指令碼demo_lt.jmx;或拷貝附錄中的指令碼程式碼,另存為demo_lt.jmx
注:
(1) 本測試指令碼在前文《Apache JMeter功能測試快速入門》基礎上略作修改。
(2) 在Windows下執行JMeter,當併發執行緒數設定較高時,會產生Address already in use的異常。推薦在Linux下以命令列模式執行測試指令碼進行壓力測試。
     為簡單起見,本示例仍在Win 10下執行。

指令碼配置

在Windows下開啟apache-jmeter-3.2,雙擊bin子目錄下的jmeter.bat(注意要有JAVA_HOME環境變數),開啟demo_lt.jmx。

主要配置有:
(1) 執行緒組設定

圖1 執行緒組設定

執行緒屬性解釋如下
執行緒數 : 一個執行緒模擬一個使用者 ,50個執行緒就是模擬50個使用者。
Ramp-Up Period: 設定執行緒需要多長時間全部啟動。如果執行緒數為50,準備時長為5,那麼需要平均每秒鐘啟動10個執行緒。
迴圈次數 : 每個執行緒每個Sampler請求執行的次數 。如果勾選了“永遠”,那麼所有執行緒會一直髮送請求,直到選擇停止執行。

(2) 定時器設定

圖2 定時器設定

定時器在每個 Sampler(取樣器)之前執行,我們此處設定的是高斯隨機定時器,每個執行緒在請求前按隨機時間停頓,上圖表示暫停時間分佈在300到500毫秒之間。
注:
因為現實中連續兩次操作是有時間間隔的,此處設定定時器是為了模擬人連續操作的思考時間。

GUI模式執行

點選工具欄中的啟動按鈕,或按Ctrl+R,執行測試計劃。在導航欄點選“Aggregate Graph”,結果如下圖所示。

圖3. Aggregate Graph

Aggregate Graph主要欄位說明如下
Label: 定義的取樣器名稱
Samples: 表示這次測試中一共發出了多少個請求
Average: 請求的平均響應時間
Min: 請求的最小響應時間
Max: 請求的最大響應時間
Error%: 錯誤的請求的數量/請求的總數
Throughput:每秒完成的請求數
KB/Sec: 每秒從伺服器端接收到的資料量

命令列執行

(1) 執行
開啟命令列視窗,切換到JMeter的bin目錄,執行如下命令:
jmeter.bat -n -t demo_lt.jmx -l demo_lt.jtl -e -o C:\lt
注:
此處指定結果檔案為demo_lt.jmx,統計目錄為C:\lt

(2) 檢視執行結果
執行JMeter,在導航欄中點選“ 檢視結果樹”,主介面中點選“瀏覽”,選擇生成的結果檔案demo_lt.jtl,效果如下圖所示:


圖4 檢視結果樹

(3) 檢視統計結果
開啟目錄C:\lt,點選該目錄下的index.html,可以看到詳細的統計結果。下面兩張圖分別截取了效能指標和綜合統計資訊部分。


圖5 效能指標


圖6 綜合統計資訊

附錄

A.1 demo_lt.jmx
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.2 r1790748">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="測試計劃" enabled="true">
      <stringProp name="TestPlan.comments">demo by weichen</stringProp>
      <boolProp name="TestPlan.functional_mode">false</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>
      <SetupThreadGroup guiclass="SetupThreadGroupGui" testclass="SetupThreadGroup" 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>
          <stringProp name="LoopController.loops">10</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">50</stringProp>
        <stringProp name="ThreadGroup.ramp_time">5</stringProp>
        <longProp name="ThreadGroup.start_time">1501545600000</longProp>
        <longProp name="ThreadGroup.end_time">1501545600000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <stringProp name="TestPlan.comments">mu mu</stringProp>
      </SetupThreadGroup>
      <hashTree>
        <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie 管理器" enabled="true">
          <collectionProp name="CookieManager.cookies"/>
          <boolProp name="CookieManager.clearEachIteration">false</boolProp>
          <stringProp name="CookieManager.policy">rfc2109</stringProp>
          <stringProp name="CookieManager.implementation">org.apache.jmeter.protocol.http.control.HC4CookieHandler</stringProp>
        </CookieManager>
        <hashTree/>
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP資訊頭管理器" enabled="true">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">apikey</stringProp>
              <stringProp name="Header.value">89db1e77a8b94c4017f551c28082eb86</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
        <hashTree/>
        <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="使用者定義的變數" enabled="true">
          <collectionProp name="Arguments.arguments">
            <elementProp name="server" elementType="Argument">
              <stringProp name="Argument.name">server</stringProp>
              <stringProp name="Argument.value">heweather</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
          </collectionProp>
        </Arguments>
        <hashTree/>
        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="伺服器" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="使用者定義的變數" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">apis.baidu.com</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path"></stringProp>
          <stringProp name="HTTPSampler.concurrentPool">4</stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </ConfigTestElement>
        <hashTree/>
        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="伺服器(測試環境)" enabled="false">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="使用者定義的變數" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">apis.baidu.com</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path"></stringProp>
          <stringProp name="HTTPSampler.concurrentPool">4</stringProp>
          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </ConfigTestElement>
        <hashTree/>
        <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="響應斷言" enabled="true">
          <collectionProp name="Asserion.test_strings">
            <stringProp name="49586">200</stringProp>
          </collectionProp>
          <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
          <boolProp name="Assertion.assume_success">false</boolProp>
          <intProp name="Assertion.test_type">8</intProp>
        </ResponseAssertion>
        <hashTree/>
        <GaussianRandomTimer guiclass="GaussianRandomTimerGui" testclass="GaussianRandomTimer" testname="高斯隨機定時器" enabled="true">
          <stringProp name="ConstantTimer.delay">500</stringProp>
          <stringProp name="RandomTimer.range">300.0</stringProp>
        </GaussianRandomTimer>
        <hashTree/>
        <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="百度APIStore天氣預報介面" enabled="true"/>
        <hashTree>
          <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="國內景點天氣預報" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="使用者定義的變數" enabled="true">
              <collectionProp name="Arguments.arguments">
                <elementProp name="cityid" elementType="HTTPArgument">
                  <boolProp name="HTTPArgument.always_encode">false</boolProp>
                  <stringProp name="Argument.value">CN10101010018A</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                  <stringProp name="Argument.name">cityid</stringProp>
                </elementProp>
              </collectionProp>
            </elementProp>
            <stringProp name="HTTPSampler.domain"></stringProp>
            <stringProp name="HTTPSampler.port"></stringProp>
            <stringProp name="HTTPSampler.protocol"></stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">${server}/pro/attractions</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="城市天氣高階資料" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="使用者定義的變數" enabled="true">
              <collectionProp name="Arguments.arguments">
                <elementProp name="city" elementType="HTTPArgument">
                  <boolProp name="HTTPArgument.always_encode">false</boolProp>
                  <stringProp name="Argument.value">蘇州</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                  <stringProp name="Argument.name">city</stringProp>
                </elementProp>
              </collectionProp>
            </elementProp>
            <stringProp name="HTTPSampler.domain"></stringProp>
            <stringProp name="HTTPSampler.port"></stringProp>
            <stringProp name="HTTPSampler.protocol"></stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">${server}/pro/weather</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/>
        </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>false</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
              <responseDataOnError>false</responseDataOnError>
              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
              <assertionsResultsToSave>0</assertionsResultsToSave>
              <bytes>true</bytes>
              <threadCounts>true</threadCounts>
            </value>
          </objProp>
          <stringProp name="filename"></stringProp>
        </ResultCollector>
        <hashTree/>
        <ResultCollector guiclass="StatGraphVisualizer" testclass="ResultCollector" testname="Aggregate Graph" 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>false</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
              <responseDataOnError>false</responseDataOnError>
              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
              <assertionsResultsToSave>0</assertionsResultsToSave>
              <bytes>true</bytes>
              <threadCounts>true</threadCounts>
            </value>
          </objProp>
          <stringProp name="filename"></stringProp>
        </ResultCollector>
        <hashTree/>
      </hashTree>
    </hashTree>
    <WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="工作臺" enabled="true">
      <boolProp name="WorkBench.save">true</boolProp>
    </WorkBench>
    <hashTree/>
  </hashTree>
</jmeterTestPlan>