如何快速調(diào)度 PTS 的百萬并發(fā)能力
發(fā)布時(shí)間:2022-01-04 點(diǎn)擊數(shù):817
作者:靈苒
業(yè)界常見的壓測軟件 JMeter 和 PTS
Cloud Native
目前 JMeter 是性能壓測領(lǐng)域應(yīng)用最廣泛的開源軟件。
對(duì)于場景簡單,要求測試并發(fā)量不高的情況下,JMeter 本地測試就能滿足需求。但隨著互聯(lián)網(wǎng)用戶的增加,對(duì)系統(tǒng)承載更大并發(fā)的需求日漸提升,而單臺(tái) JMeter 施壓機(jī)的施壓能力有一定上限,所以需要使用多臺(tái)施壓機(jī),以提高 JMeter 的施壓能力,這就要使用到 JMeter 的分布式施壓功能。
但 JMeter 的分布式壓測前置準(zhǔn)備較多,需要注意以下幾點(diǎn):
- 施壓機(jī)的防火墻已關(guān)閉或打開了正確的端口。為 RMI 設(shè)置了 SSL 或禁用了它。
- 所有施壓機(jī)都在同一個(gè)子網(wǎng)上。如果使用 192.xxx或10.xxx IP 地址,則服務(wù)器位于同一子網(wǎng)中。
- 所有施壓機(jī)上使用相同版本的 JMeter 和 Java。
- 所有施壓機(jī)都已經(jīng)拷貝了切分好的 CSV 數(shù)據(jù)文件、依賴 jar 包等。
- 已配置好監(jiān)控?cái)?shù)據(jù)的收集。
由此可見 JMeter 的分布式壓測需要自己協(xié)調(diào)各資源,前置準(zhǔn)備比較麻煩,對(duì)實(shí)施壓測的人員來說壓測效率低。
PTS 的 JMeter 壓測極大的簡化了 JMeter 分布式壓測流程,同時(shí)也降低了壓測過程中對(duì)施壓機(jī)的維護(hù)成本。使用 PTS 的 JMeter 壓測,用戶只需要在控制臺(tái)配置需要使用的機(jī)器數(shù),無須用戶提前準(zhǔn)備多臺(tái)已安裝相同 Java 和 JMeter 版本的施壓機(jī)。同時(shí)無須用戶根據(jù)施壓機(jī)數(shù)量去切分 CSV 參數(shù)文件;壓測結(jié)束后,PTS 會(huì)將監(jiān)控?cái)?shù)據(jù)匯總產(chǎn)生一個(gè)詳細(xì)的壓測報(bào)告供用戶查閱。
相比于直接在命令行執(zhí)行 JMeter 腳本來說,PTS 使用更加方便,可按需提供海量的施壓能力,并且能提供簡潔直觀的監(jiān)控和報(bào)告。
如何發(fā)起 PTS 的 JMeter 壓測
Cloud Native
和所有壓測的核心步驟一樣,使用 PTS 的 JMeter 壓測,也主要集中在創(chuàng)建場景、壓測場景和查看報(bào)告三個(gè)步驟中。
1、創(chuàng)建場景:PTS 的 JMeter 壓測以場景為核心,壓測對(duì)象為一個(gè)場景,場景中包括JMeter(原生)腳本、JMeter 依賴(一系列依賴 jar 包和一系列 properties 配置)、及一些壓測配置(PTS 壓測的配置,例如公網(wǎng)/VPC 壓測、并發(fā)量、引擎數(shù)量、壓測時(shí)長等)。
2、壓測場景:對(duì)場景的操作分為兩方面,一是對(duì)場景配置的增刪改查,二是對(duì)場景的壓測和調(diào)試。
3、生成報(bào)告:每次對(duì)場景壓測都會(huì)生成一個(gè)壓測任務(wù),同時(shí)生成一個(gè)報(bào)告,其中包括壓測的關(guān)鍵指標(biāo),如 TPS、RT、成功率等,可輔助用戶排查系統(tǒng)性能瓶頸。此外,PTS 默認(rèn)將報(bào)告保存 30 天,可以隨時(shí)查看歷史報(bào)告,并且提供導(dǎo)出 PDF 格式的報(bào)告。
所以,為了方便用戶便捷調(diào)度 PTS 百萬并發(fā)的能力,PTS 開通了 JMeter 的 OpenAPI,提供了如下幾類壓測的核心功能:編輯場景、調(diào)試場景、壓測場景、查看運(yùn)行時(shí)數(shù)據(jù)、查看報(bào)告。
附錄:
具體步驟如下:
1 引入 pom 依賴
<!--創(chuàng)建PTS場景需要的實(shí)體類,如果只使用JMeter壓測則不需要引入--><dependency> <groupId>com.aliyun</groupId> <artifactId>pts-api-entity</artifactId> <version>1.0.1</version></dependency><!--PTS Java SDK依賴。--><dependency> <groupId>com.aliyun</groupId> <artifactId>pts20201020</artifactId> <version>1.8.10</version></dependency><!--阿里云核心庫。--><dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.5.2</version></dependency>
2 復(fù)制下列代碼
import com.aliyun.pts20201020.Client;import com.aliyun.pts20201020.models.*;import com.aliyun.teaopenapi.models.Config; import java.util.ArrayList;import java.util.List;import java.util.Map; public class StartingDemo { public static void main(String[] args) throws Exception { Client client = getClient(); // 創(chuàng)建場景 String sceneId = createScene(client); // 啟動(dòng)場景 String reportId = startTesting(client, sceneId); // 最多等待次數(shù) int count = 0; // 查詢是否已生成報(bào)告 while (!hasReport(client, reportId) && count++ < 20) { // 若報(bào)告還未生成,則等待(30s)一段時(shí)間再查詢 // 根據(jù)壓測時(shí)間酌情等待 Thread.sleep(30 * 1000); } // 查看報(bào)告 getJMeterReport(client, reportId); } private static boolean hasReport(Client client, String reportId) throws Exception { ListJMeterReportsRequest request = new ListJMeterReportsRequest(); // 分頁設(shè)置 request.setPageNumber(1); request.setPageSize(1); // 查詢條件設(shè)置 request.setReportId(reportId); ListJMeterReportsResponse response = client.listJMeterReports(request); return response.getBody().getReports().size() > 0; } private static void getJMeterReport(Client client, String reportId) throws Exception { // 查看機(jī)器日志 GetJMeterLogsResponse getJMeterLogsResponse = getJMeterLogs(client, reportId); List<Map<String, ?>> logs = getJMeterLogsResponse.getBody().getLogs(); // 查看采樣器聚合數(shù)據(jù) GetJMeterSampleMetricsResponse getJMeterSampleMetrics = getJMeterSampleMetrics(client, reportId); List<String> sampleMetricList = getJMeterSampleMetrics.getBody().getSampleMetricList(); // 查看采樣日志 GetJMeterSamplingLogsResponse getJMeterSamplingLogs = getJMeterSamplingLogs(client, reportId); List<String> sampleResults = getJMeterSamplingLogs.getBody().getSampleResults(); } private static GetJMeterSamplingLogsResponse getJMeterSamplingLogs(Client client, String reportId) throws Exception { GetJMeterSamplingLogsRequest request = new GetJMeterSamplingLogsRequest(); // 分頁設(shè)置 request.setPageNumber(1); request.setPageSize(10); // 條件設(shè)置 request.setReportId(reportId); GetJMeterSamplingLogsResponse response = client.getJMeterSamplingLogs(request); return response; } private static GetJMeterSampleMetricsResponse getJMeterSampleMetrics(Client client, String reportId) throws Exception { GetJMeterSampleMetricsRequest request = new GetJMeterSampleMetricsRequest(); // 設(shè)置報(bào)告id request.setReportId(reportId); GetJMeterSampleMetricsResponse response = client.getJMeterSampleMetrics(request); return response; } private static GetJMeterLogsResponse getJMeterLogs(Client client, String reportId) throws Exception { GetJMeterLogsRequest request = new GetJMeterLogsRequest(); // 分頁設(shè)置 request.setPageNumber(1); request.setPageSize(10); // 查詢的壓測引擎索引 request.setReportId(reportId); GetJMeterLogsResponse response = client.getJMeterLogs(request); return response; } private static String startTesting(Client client, String sceneId) throws Exception { StartTestingJMeterSceneResponse startTestingSceneResponse = startTestingScene(client, sceneId); String reportId = startTestingSceneResponse.getBody().getReportId(); return reportId; } private static StartTestingJMeterSceneResponse startTestingScene(Client client, String sceneId) throws Exception { StartTestingJMeterSceneRequest request = new StartTestingJMeterSceneRequest(); request.setSceneId(sceneId); StartTestingJMeterSceneResponse response = client.startTestingJMeterScene(request); return response; } private static String createScene(Client client) throws Exception { SaveOpenJMeterSceneRequest request = new SaveOpenJMeterSceneRequest(); // 定義場景 SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterScene scene = new SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterScene(); // 設(shè)置場景名 scene.setSceneName("test"); // 設(shè)置文件列表,包括JMeter腳本、JMeter壓測依賴jar包、配置額度數(shù)據(jù)文件等 List<SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList> fileList = new ArrayList<SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList>(); // 設(shè)置文件的屬性 需要設(shè)置文件的名稱和文件公網(wǎng)可訪問的oss地址 SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList testFile = new SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList(); testFile.setFileName("baidu.jmx"); testFile.setFileOssAddress("https://pts-openapi-test.oss-cn-shanghai.aliyuncs.com/baidu.jmx"); fileList.add(testFile); scene.setFileList(fileList); // 設(shè)置場景并發(fā),可設(shè)置為100萬 scene.setConcurrency(1000000); // 設(shè)置引擎數(shù)量 說明:一臺(tái)引擎最多能發(fā)500并發(fā),最少1并發(fā)所以此處能設(shè)置的引擎數(shù)為[2,1000],另外引擎數(shù)量越多消耗vum越快 scene.setAgentCount(2000); // 設(shè)置壓測持續(xù)時(shí)間 60s scene.setDuration(60); // 設(shè)置測試文件的名稱,這個(gè)文件需包括在文件列表中 scene.setTestFile("baidu.jmx"); request.setOpenJMeterScene(scene); SaveOpenJMeterSceneResponse response = client.saveOpenJMeterScene(request); return response.getBody().getSceneId(); } private static Client getClient() throws Exception { // 填寫自己的AK/SK String accessKeyId = "ak"; String accessKeySecret = "sk"; Config config = new Config(); config.setAccessKeyId(accessKeyId); config.setAccessKeySecret(accessKeySecret); Client client = new Client(config); return client; }}
3 填寫自己的 ak/sk
在上述代碼的 getClient 中填寫正確的 ak/sk
4 點(diǎn)擊啟動(dòng)