需求背景
新業務上線前,我們通常需要對系統的不同中間件進行壓測,找到當前配置下中間件承受流量的上限,從而確定上游鏈路的限流規則,保護系統不因突發流量而崩潰。阿里雲 PTS 的 JMeter 壓測可以支持用戶上傳自定義的 JMeter 腳本,按照自定義的邏輯,藉助 PTS 強大的分佈式壓測能力,對系統的不同中間件進行壓測。下面,將以 JMeter5.5 和 RocketMQ5.0 系列爲例,詳細介紹如何使用 PTS 的 JMeter 場景壓測 RocketMQ。
前置條件
1. 已在本地安裝 JMeter。
2. 已在阿里雲 ECS 上部署 RocketMQ(本文選擇的是一臺 8C32G 規格的 ECS)。
3. 已在阿里雲上開通 PTS 服務。
壓測過程
JMeter 提供了擴展性極強的 JavaSampler,我們可以通過繼承 AbstractJavaSamplerClient 類來自定義在 JavaSampler 中執行的邏輯,從而實現對 RocketMQ 進行壓測。
步驟一:創建 Maven 項目,並引入依賴
1. 新建 Maven 工程,並在 pom 文件中引入下面的依賴:
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>5.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.9.5</version>
</dependency>
ApacheJMeter_java 是 JMeter JavaSampler 的依賴,rocketmq-client 是 RocketMQ 的客戶端依賴(此處用 4.x 版本是因爲 4.x 版本的客戶端可以兼容 5.x 版本的服務端實例,但是 5.x 版本的客戶端不能兼容 4.x 版本的服務端實例,可根據自己需求調整)。其中,要注意的是 ApacheJMeter_java 依賴的 scope 定義爲 provided,JMeter 的 lib/ext 目錄下已有該 JAR 包,因此不必將該依賴一起打包。
2. 在 pom 文件中引入 maven-assembly-plugin 插件,此處使用 “jar-with-dependencies” 打包方式,將項目所需依賴和項目代碼打包到同一個 JAR 包,後續可以只上傳該 JAR 包到 PTS 的 JMeter 環境中,不用上傳多個依賴 JAR 包:
<build>
<finalName>jmeter-rocketmq4</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<!-- 打包方式 -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
步驟二:新建 AbstractJavaSamplerClient 的子類,並重寫相關方法
AbstractJavaSamplerClient 類繼承了 JavaSamplerClient 接口,該接口包含 setupTest、runTest、teardownTest 和 getDefaultParameters 四個方法:
- setupTest
JMeter 將爲測試中的每個線程創建一個 JavaSamplerClient 實現實例,測試開始時,將在每個線程的 JavaSamplerClient 實例上調用 setupTest 來初始化客戶端,本例中即初始化 RocketMQ 的 producer。
- runTest
每個線程每次迭代會調用一次 runTest 方法,本例中,需要在 runTest 方法裏面定義消息發送的方法和採樣結果的設置邏輯。
- teardownTest
迭代完設置的次數或時間後,此方法將會被執行,本例中,需要在此方法關閉 producer。
- getDefaultParameters
此方法定義了參數列表,這些參數通過會 JavaSamplerContext 傳遞給上述方法方法,在此方法內定義的參數,可以在 JMeter JavaRequest Sampler 的 GUI 界面設置值,本例中,需要定義 RocketMQ 的 broker 地址、topic 名稱、消息 key、消息內容等參數。
新建子類參考如下:
import java.nio.charset.StandardCharsets;
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.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
public class JavaSamplerForRocketMQ extends AbstractJavaSamplerClient {
private DefaultMQProducer producer;
private static final String NAME_SRV_ADDRESS = "nameSrvAddress";
private static final String TOPIC = "topic";
private static final String PRODUCER_GROUP = "producer group";
private static final String MSG_BODY = "messageBody";
private static final String MSG_KEY = "messageKey";
private static final String MSG_TAG = "messageTag";
private static final String ERROR_CODE = "500";
@Override
public void setupTest(JavaSamplerContext javaSamplerContext) {
try {
// 初始化producer
producer = new DefaultMQProducer(javaSamplerContext.getParameter(PRODUCER_GROUP));
producer.setNamesrvAddr(javaSamplerContext.getParameter(NAME_SRV_ADDRESS));
producer.start();
} catch (MQClientException e) {
throw new RuntimeException(e);
}
}
@Override
public SampleResult runTest(JavaSamplerContext javaSamplerContext) {
SampleResult sampleResult = new SampleResult();
sampleResult.setSampleLabel("rocketmq-producer");
// 請求開始
sampleResult.sampleStart();
// 普通消息發送
Message message = new Message(
javaSamplerContext.getParameter(TOPIC),
javaSamplerContext.getParameter(MSG_TAG),
javaSamplerContext.getParameter(MSG_BODY).getBytes()
);
try {
// 發送消息,需要關注發送結果,並捕獲失敗等異常。
SendResult sendResult = producer.send(message);
// 設置發送請求的字節數
sampleResult.setSentBytes(message.toString().getBytes(StandardCharsets.UTF_8).length);
sampleResult.setDataType(SampleResult.TEXT);
// 設置請求內容
sampleResult.setSamplerData(message.toString());
// 設置響應內容
sampleResult.setResponseData(String.format("Msg Id:%s", sendResult.getMsgId()).getBytes());
sampleResult.setSuccessful(true);
sampleResult.setResponseCodeOK();
} catch (MQBrokerException | InterruptedException | RemotingException | MQClientException e) {
sampleResult.setSuccessful(false);
sampleResult.setResponseCode(ERROR_CODE);
sampleResult.setResponseData(String.format("Error Msg:%s", e).getBytes());
return sampleResult;
} finally {
// 請求結束
sampleResult.sampleEnd();
}
return sampleResult;
}
@Override
public void teardownTest(JavaSamplerContext javaSamplerContext) {
producer.shutdown();
}
@Override
public Arguments getDefaultParameters() {
Arguments arguments = new Arguments();
arguments.addArgument(NAME_SRV_ADDRESS, "");
arguments.addArgument(PRODUCER_GROUP, "");
arguments.addArgument(TOPIC, "");
arguments.addArgument(MSG_KEY, "");
arguments.addArgument(MSG_TAG, "");
arguments.addArgument(MSG_BODY, "");
return arguments;
}
}
步驟三:打包項目成 JAR 文件
通過 mvn clean package 將項目打包,在 target 目錄中可見 jmeter-rocketmq4.jar 和 jmeter-rocketmq4-jar-with-dependencies.jar 兩個 JAR 包,其中 jmeter-rocketmq4-jar-with-dependencies.jar 包括了所需的依賴,在後續步驟中使用此 JAR 包。
.
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── JavaSamplerForRocketMQ4.java
│ │ └── resources
│ └── test
│ └── java
└── target
├── jmeter-rocketmq4-jar-with-dependencies.jar
├── jmeter-rocketmq4.jar
步驟四:使用 JMeter GUI 進行腳本編寫和調試
1. 將打包好的 JAR 包和依賴的 JAR 包複製到 JMETER_HOME/lib/ext 目錄下,然後執行命令 JMETER_HOME/bin/jmeter 打開 JMeter GUI。
2. 新建線程組後添加 Java 請求取樣器。
3. 在下拉框中選擇步驟二中新增的類(不一定和圖片中的完全一致,按照實際的類全限定名選擇),並填寫下方相關參數。
4. 爲線程組添加“查看結果樹”和“彙總報告”監聽器,然後啓動測試計劃,在結果樹和彙總報告中驗證測試的結果是否符合預期。
5. 保存該測試計劃爲 JMX 文件。
步驟五:在 PTS 創建 JMeter 場景進行壓測
1. 在 PTS 控制檯創建 JMeter 環境,將步驟三中打包的 JAR 包上傳到該 JMeter 環境中(更多細節請參考 JMeter 環境管理的查看、修改及創建_性能測試-阿里雲幫助中心[1]):
a. 進入 PTS 控制檯,選擇“JMeter 環境”;
b. 輸入自定義的環境名;
c. 點擊上傳文件,選擇步驟三中打包的 JAR 包;
d. 點擊保存。
2. 在 PTS 控制檯創建場景中選擇“JMeter 壓測”場景:
3. 編輯“場景配置”:
a. 自定義場景名;
b. 點擊上傳文件,選擇步驟四中保存的 JMX 文件;
c. 在“使用依賴環境?”下拉框中選擇“是,使用依賴環境”;
d. 在“選擇依賴環境”下拉框選擇剛剛創建的 JMeter 環境。
4. 施壓配置:
小建議:由於我們是想通過壓測找到 RocektMQ 能承受的最大併發請求數,因此建議選擇 RPS 模式,這樣可以直接衡量 RocektMQ 的承壓能力。同時,考慮到公網帶寬限制,應該選擇阿里雲 VPC 內網壓測。
a. 選擇壓力來源爲阿里雲 VPC 內網,同時選擇部署被壓測 RocketMQ 的 ECS 所在區域;
b. 設置 ECS 的 VPC、安全組和交換機,注意 VPC 和安全組一定要和 ECS 相同,安全組中要打開響應的端口(在 ECS 控制檯設置);
c. 設置壓力模式爲 RPS 模式;
d. 設置起始 RPS、最大 RPS 和壓測時長,本文設置起始 RPS 爲 90000,最大 RPS 爲 110000,持續 2 分鐘。
e. 指定循環一般設置爲否,表示執行一次就結束,指定 IP 數會根據設置的 RPS 自動生成。
5. 其餘設置請根據需求參考 JMeter 壓測_性能測試-阿里雲幫助中心[2]。6. 保存配置並調試場景,確認和 RocketMQ 的連通,之後可以開始進行壓測。
步驟六:查看壓測報告
JMeter 的壓測報告通用解讀可以參考如何查看 JMeter 壓測數據、採樣日誌及施壓機性能_性能測試-阿里雲幫助中心[3],下一節將介紹如何使用 PTS 的壓測報告來找到 RocketMQ 的承壓能力。
報告解讀
1. 首先,查看整個壓測的概覽信息和指標趨勢。如下圖所示,報告第一欄展示了整個壓測過程的請求成功率、平均 RT、平均 TPS 等指標,這些指標可以在官方文檔中找到具體解釋。同時,根據成功率的趨勢圖所示,從 18:54:05 開始,成功率逐漸波動下降,此時的 TPS 值爲 9.55W,代表 18:54:05 計算的前 5 秒平均 TPS 約爲 9.55W。
2. 其次,使用壓測報告中的 Prometheus 監控數據對結果進一步分析。藉助阿里雲 ARMS 的 Prometheus 和 Grafana 產品,PTS 的壓測報告可以提供包括吞吐量、成功率和響應時長的時序圖,同時,支持用戶使用 PromQL 語句對數據面板進行編輯操作,靈活查詢所需的數據,在本文中,我們可以將成功率和吞吐量放在一個 panel,來進一步分析。
a. 首先點擊“成功率(時序)”,然後點擊“Edit”,可進入成功率大盤的編輯界面,複製成功率的查詢 PromQL:
sum(rate(pts_api_response_total{task_id="$task_id", code=~"200|302"}[5s]))/sum(rate(pts_api_response_total{task_id="$task_id"}[5s]))
b. 然後進入吞吐量大盤的編輯界面,使用成功率的 PromQL 替換虛擬用戶數的 PromQL,並更改 Grafana 的相關配置(下圖中紅框),便可得到展示吞吐量和成功率的面板。
該面板展示的數據統計精度爲 1 秒,可得到更精確的數據,在 18:54:05 秒時,成功率開始下降,此時 TPS 爲 96561.9。
c. 爲了更好的評估 RocketMQ 的性能,我們還可以統計出成功率保持 100% 的時間範圍內的平均 TPS,首先找到成功率爲 100% 的持續時間,下圖中爲 47 秒,然後將計算 TPS 的指標的時間範圍改成 47s,這樣每個點都代表前 47s 的平均 TPS,將鼠標移動到成功率爲 100% 的最後一個時間,當前時間的 TPS 值即爲成功率爲 100% 時間範圍內的平均 TPS,即 89357.5。
3. 最後,爲了對比不同參數的設置對 RocketMQ 性能的影響,同時驗證 PTS 在 RocketMQ 壓測上的可用性,我們做了一個簡單的對比實驗,並通過 jstat 命令來觀察不同參數對垃圾回收的影響。
實驗結果顯示,對於當前 ECS 配置部署的 RocketMQ,適當調大堆內存可以有效提高 RocketMQ 的性能,當堆內存提高到 24g 時(此事 ECS 內存使用率達到 85.39%),性能沒有顯著提高;適當提高 sendMessageThreadPoolNums 的值可以提高 RocketMQ 的性能,當 sendMessageThreadPoolNums 超過 16 後,性能沒有顯著提高,甚至略有下降。用戶可以根據實際情況,進行更詳細的對比實驗,來充分評估所部署的 RocketMQ 承壓能力。
結束語
本文介紹了使用阿里雲 PTS 的 JMeter 場景壓測 RocketMQ 的詳細步驟,對各環節逐一進行了說明,最後,通過對壓測報告的自定義分析,展現了 PTS 強大的壓測結果分析能力,藉助 JMeter 和 PTS,用戶可以對各類中間件進行靈活多維的分析,助力其構建起穩定健壯的系統。
相關鏈接:
[1] JMeter 環境管理的查看、修改及創建_性能測試-阿里雲幫助中心
https://help.aliyun.com/document_detail/170857.html?spm=a2c4g.103173.0.0.292c20f8wnWyCV
[2] JMeter 壓測_性能測試-阿里雲幫助中心
https://help.aliyun.com/document_detail/97876.html?spm=a2c4g.91788.0.0.2fde6f338aHIDI
[3] 如何查看 JMeter 壓測數據、採樣日誌及施壓機性能_性能測試-阿里雲幫助中心
https://help.aliyun.com/document_detail/127454.html?spm=a2c4g.94066.0.0.4a5164bepHmzWD
作者:森元
本文爲阿里雲原創內容,未經允許不得轉載。