通過jmeter測試Java性能
對java與scala等代碼或客戶端的性能測試,不能直接發起http請求,需要實例化對象發送請求實現性能測試。
- jmeter提供的Java Request取樣器可以控制實現JavaSamplerClient接口的類,輸入參數並獲取響應,利用多線程進行性能測試。
- 通過maven插件啟動jmeter,配合部分插件管理依賴,簡化每次測試環境的准備工作。
環境搭建
1. 搭建方式
手動配置環境:
- 創建測試項目
- 復制jmeter lib與lib\ext目錄下的依賴文件添加到當前工程,如eclipse:鼠標選中工程點擊右鍵 -> Build Path -> Configure Build Path -> 彈出框Libraries Tab -> 點擊 Add External JARs -> 切換到待導入路徑選取依賴jar。
- 創建測試類實現JavaSamplerClient接口或繼承AbstractJavaSamplerClient,並重寫。 並不推薦使用實現接口的方式,使用集成AbstractJavaSamplerClient的方式比較好。
- 編譯源碼,導出為Runnable Jar File
- 將編譯后jar包放入jmeter lib\ext目錄,項目依賴jar包放入jmeter lib目錄,復制項目依賴配置文件
- 啟動jmeter,創建測試計划進行測試。
通過maven構建配置環境:
- 創建測試項目
- 添加jmeter dependency
- 添加並配置插件:jmeter-maven-plugin(maven啟動jmeter)、maven-dependency-plugin(copy依賴)、maven-dependency-plugin(打包項目並移動到制定目錄)、maven-resources-plugin(copy項目依賴文件)
- 創建測試類實現JavaSamplerClient接口或繼承AbstractJavaSamplerClient,並重寫所需測試方法。
- 啟動jmeter,創建測試計划進行測試。
上面兩中方式啟動jmeter測試都可以:
- 手動配置環境:從命令行啟動jmeter,將所有依賴都copy進jmeter項目,對於需要較大並發的分布式測試比較好,但是每次修改變動都需要重新打包copy。
- 通過maven構建配置環境:在項目內啟動jmeter,只需要在pom.xml中配置依賴,每次有修改重新構建並啟動便可,但是分布式的測試仍然需要在分布機器上發送依賴包。
調試腳本與小並發的測試通過maven比較方便,在調試好之后將依賴分發到分布式機器上通過命令行啟動agent。
2. maven配置
在maven的pom.xml文件中如下根據自己需要配置依賴文件和變量便可。
- 依賴:org.apache.jmeter組織下添加的這三個依賴是編寫Java Request擴展的必備。
- jmeter-maven-plugin:maven項目中啟動jmeter的插件。詳細使用方法:插件配置官方文檔
- maven-dependency-plugin:maven構建依賴copy插件。
- maven-antrun-plugin:ant插件,對編譯后生成的文件再操作。
- maven-resources-plugin:有些不是jmeter的配置依賴,在項目test/jmeter目錄下無法自動copy到jmeter/bin目錄下,使用這個插件copy。
注意:maven版本需要與插件兼容,如過不兼容jmeter插件是無法運行的,如jmeter-maven-plugin:2.9.0需要maven 3.6.0+
- <!-- 變量配置 -->
- <properties>
- <!-- version -->
- <jmeter.version>5.0</jmeter.version>
- <jmeter.maven.plugin.version>2.9.0</jmeter.maven.plugin.version>
- <maven.dependency.plugin.version>3.1.0</maven.dependency.plugin.version>
- <!-- 測試結果存放路徑 -->
- <jmeter.result.jtl.dir>${project.build.directory}/jmeter/results</jmeter.result.jtl.dir>
- <!-- 測試報表存放路徑 -->
- <jmeter.result.html.dir>${project.build.directory}/jmeter/html</jmeter.result.html.dir>
- </properties>
- <!-- Java Request Sample依賴添加 -->
- <dependencies>
- <dependency>
- <groupId>org.apache.jmeter</groupId>
- <artifactId>ApacheJMeter_core</artifactId>
- <version>${jmeter.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.jmeter</groupId>
- <artifactId>ApacheJMeter_java</artifactId>
- <version>${jmeter.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.jmeter</groupId>
- <artifactId>jorphan</artifactId>
- <version>${jmeter.version}</version>
- </dependency>
- </dependencies>
- <!-- 構建插件 -->
- <build>
- <plugins>
- <!-- jmeter插件 -->
- <plugin>
- <groupId>com.lazerycode.jmeter</groupId>
- <artifactId>jmeter-maven-plugin</artifactId>
- <version>${jmeter.maven.plugin.version}</version>
- <executions>
- <execution>
- <id>jmeter-tests</id>
- <goals>
- <goal>jmeter</goal>
- </goals>
- </execution>
- <!-- 設置ignoreResultFailures,必須把 jmeter-check-results加上-->
- <execution>
- <id>jmeter-check-results</id>
- <goals>
- <goal>results</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <!-- 指定jmeter版本 -->
- <jmeterVersion>${jmeter.version}</jmeterVersion>
- <!-- 聚合報告會覆蓋generateReports,指定生成CSV結果 -->
- <generateReports>true</generateReports>
- <resultsFileFormat>csv</resultsFileFormat>
- <!-- 設置忽略失敗是否停止運行 -->
- <ignoreResultFailures>true</ignoreResultFailures>
- <!-- 設置結果文件末尾時間戳 -->
- <testResultsTimestamp>true</testResultsTimestamp>
- <!-- 時間戳格式 -->
- <resultsFileNameDateFormat>Y-M-D H:m:s</resultsFileNameDateFormat>
- <testFilesIncluded>
- <!-- 指定運行的jmeter腳本 -->
- <jMeterTestFile>JmeterTest.jmx</jMeterTestFile>
- </testFilesIncluded>
- <!-- 指定結果生成目錄 -->
- <resultsDirectory>${project.build.directory}/jmeter/results</resultsDirectory>
- <!-- JVM參數 -->
- <jMeterProcessJVMSettings>
- <xms>2048</xms>
- <xmx>4096</xmx>
- <arguments>
- <argument>-Xprof</argument>
- <argument>-Xfuture</argument>
- </arguments>
- </jMeterProcessJVMSettings>
- </configuration>
- </plugin>
- <!--復制copy依賴文件-->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-resources-plugin</artifactId>
- <executions>
- <execution>
- <id>copy-resources</id>
- <phase>package</phase>
- <goals>
- <goal>copy-resources</goal>
- </goals>
- <configuration>
- <encoding>UTF-8</encoding>
- <!--輸出路徑-->
- <outputDirectory>
- ${project.build.directory}/jmeter/bin
- </outputDirectory>
- <resources>
- <resource>
- <!--項目中的路徑-->
- <directory>${project.basedir}/src/test/jmeter/</directory>
- </resource>
- </resources>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <!-- copy被測項目依賴到指定目錄 -->
- <plugin>
- <artifactId>maven-dependency-plugin</artifactId>
- <version>${maven.dependency.plugin.version}</version>
- <executions>
- <execution>
- <id>copy-dependencies</id>
- <phase>package</phase>
- <goals>
- <goal>copy-dependencies</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <overWriteSnapshots>true</overWriteSnapshots>
- <overWriteReleases>true</overWriteReleases>
- <overWriteIfNewer>true</overWriteIfNewer>
- <outputDirectory>${project.build.directory}/jmeter/lib</outputDirectory>
- </configuration>
- </plugin>
- <!-- copy編譯打包后jar文件到指定目錄:由於jar是在編譯完成之后才能拿到,所以用ant插件在編譯后copy jar -->
- <plugin>
- <artifactId>maven-antrun-plugin</artifactId>
- <executions>
- <execution>
- <phase>package</phase>
- <goals>
- <goal>run</goal>
- </goals>
- <configuration>
- <tasks>
- <mkdir dir="${project.build.directory}/jmeter/lib/ext"/>
- <copy todir="${project.build.directory}/jmeter/lib/ext" overwrite="true" >
- <fileset dir="${project.build.directory}" erroronmissingdir="false">
- <include name="*.jar"/>
- </fileset>
- </copy>
- </tasks>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
測試例子
1. 項目編寫
tess4j是Tesseract OCR API的Java JNA封裝,除了准確性也可以測試一下處理速度。
- 新建maven項目。
- 添加tess4j maven依賴,jmeter依賴參考上面的maven配置添加
- <dependency>
- <groupId>net.sourceforge.tess4j</groupId>
- <artifactId>tess4j</artifactId>
- <version>${tess4j.version}</version>
- </dependency>
- 創建工程目錄如下:
JavaRequestDemo
├── JavaRequestDemo.iml
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── Tess4jTest.java
│ └── resources
└── test
├── java
└── jmeter
├── JmeterTest.jmx
├── jmeter.properties
├── reportgenerator.properties
├── saveservice.properties
├── system.properties
├── tessdata
│ └── chi_sim.traineddata
├── upgrade.properties
└── user.properties
- 下載訓練庫,下載地址, 不同語言的識別需要下載對應的訓練庫,這里下載中文的訓練庫chi_sim.traineddata,放置到src/test/jmeter/tessdata下
- test/jmeter可以放置項依賴的各種配置文件和jmeter的配置文件,會自動copy到編譯后jmeter的bin目錄。
- 編寫測試類Tess4jTest.java
- import net.sourceforge.tess4j.ITesseract;
- import net.sourceforge.tess4j.Tesseract;
- import net.sourceforge.tess4j.TesseractException;
- import org.apache.jmeter.config.Arguments;
- import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
- import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
- import org.apache.jmeter.samplers.SampleResult;
- import org.apache.jmeter.testelement.TestElement;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.io.File;
- import java.util.Iterator;
- /**
- * public Arguments getDefaultParameters();設置可用參數及的默認值;
- * public void setupTest(JavaSamplerContext arg0):每個線程測試前執行一次,做一些初始化工作;
- * public SampleResult runTest(JavaSamplerContext arg0):開始測試,從arg0參數可以獲得參數值;
- * public void teardownTest(JavaSamplerContext arg0):測試結束時調用;
- * */
- public class Tess4jTest extends AbstractJavaSamplerClient {
- public static void main(String[] args) {
- Arguments arguments = new Arguments();
- arguments.addArgument("dataPath", "src/test/jmeter/tessdata1");
- arguments.addArgument("language", "chi_sim");
- arguments.addArgument("imagePath", "/tmp/20200119154923.jpg");
- JavaSamplerContext context = new JavaSamplerContext(arguments);
- Tess4jTest test = new Tess4jTest();
- test.setupTest(context);
- test.runTest(context);
- test.teardownTest(context);
- }
- private static final Logger LOG = LoggerFactory.getLogger(Tess4jTest.class);
- // OCR 客戶端
- private ITesseract instance = new Tesseract();
- // The name of the sampler
- private String name;
- // Java Request參數
- private String dataPath;
- private String language;
- private File image;
- /*
- * setup操作,初始化參數
- * */
-
- public void setupTest(JavaSamplerContext context) {
- if (LOG.isDebugEnabled()) {
- LOG.debug(whoAmI() + "\tsetupTest()");
- listParameters(context);
- }
- dataPath = context.getParameter("dataPath");
- language = context.getParameter("language");
- image = new File(context.getParameter("imagePath"));
- name = context.getParameter(TestElement.NAME);
- }
- /**
- * 開始測試
- * */
-
- public SampleResult runTest(JavaSamplerContext context) {
- SampleResult sample = new SampleResult();
- sample.setSampleLabel(name);
- //
- /*
- * dataPath一定要能訪問否則jvm會異常退出,可以指定相對路徑與絕對路徑,可能只是提示異常:
- * Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
- * 1. main函數中測試相對路徑是相對於src的平級目錄
- * 2. jmeter中是相對於jmeter啟動目錄,maven插件是從bin目錄啟動,所以如果使用相對路徑一般是copy到bin目錄下
- * */
- instance.setDatapath(dataPath);
- instance.setLanguage(language);
- sample.setSamplerData(image.getAbsolutePath());
- try {
- // 開始計時
- sample.sampleStart();
- // 測試的主方法
- String result = instance.doOCR(image);
- // 設置結果
- sample.setSuccessful(true);
- sample.setResponseData(result, "UTF-8");
- } catch (TesseractException e) {
- LOG.error("Tess4jTest error", e);
- sample.setSuccessful(false);
- sample.setResponseMessage(e.toString());
- } finally {
- // 計時結束
- sample.sampleEnd();
- }
- if (LOG.isDebugEnabled()) {
- LOG.debug(whoAmI() + "\trunTest()" + "\tTime:\t" + sample.getTime());
- listParameters(context);
- }
- return sample;
- }
- /*
- * 參數設置獲取
- * */
-
- public Arguments getDefaultParameters() {
- Arguments params = new Arguments();
- params.addArgument("dataPath", "tessdata");
- params.addArgument("language", "chi_sim");
- params.addArgument("imagePath", "/tmp/20200119154923.jpg");
- return params;
- }
- /**
- * 打印參數列表
- */
- private void listParameters(JavaSamplerContext context) {
- Iterator<String> argsIt = context.getParameterNamesIterator();
- while (argsIt.hasNext()) {
- String lName = argsIt.next();
- LOG.debug(lName + "=" + context.getParameter(lName));
- }
- }
- private String whoAmI() {
- return new StringBuilder()
- .append(Thread.currentThread().toString())
- .append("@")
- .append(Integer.toHexString(hashCode()))
- .toString();
- }
- }
- 測試運行,直接運行Tess4jTest.java main方法

運行結果
2. 運行測試
- 構建項目:mvn clean package
- 啟動jmeter:mvn jmeter:gui
- 編寫測試計划,單線程運行一下

測試代碼

測試結果

多添加幾個線程的聚合報告