1 前言
jmeter是性能压测领域应用最广泛开源软件,提供了非常好的扩展能力,通过社区支持非常多类型的压测,但是jmeter的分布式压测、监控等方面的能力还有待提高。而PTS(Performance Testing Service,性能测试)能够提供全国多地域、大规模分布式施压能力、全面的压测监控能力、压测分析能力,弥补了jmeter在性能压测中的劣势。所以PTS将jmeter和PTS的能力结合,各取所长,并开放了PTS的JMeter OpenAPI,方便用户直接通过引入pom的方式编码调用API。本文主要介绍如何使用 PTS的JMeter OpenAPI搭建一个压测平台,实现直接使用PTS分布式能力运行jmeter脚本进行压测。其中包含OpenAPI种类和实现的功能,以及如何使用OpenAPI。并附了一份详细的使用教程,基本可以实现填空式编程。
1.1 场景
pts的jmeter压测以场景为核心,压测对象为一个场景,场景中包括jmeter脚本、jmeter依赖、及一些压测配置,每次压测会生成一个压测任务同时生成一个报告。对场景的操作分为两方面,一是对场景配置的增删改查,二是对场景的压测和调试。
1.2 jmx脚本
即jmeter的原生脚本,可直接在jmeter的GUI页面中配置导出。
1.3 环境
环境为jmeter压测的依赖,一个环境包括一系列依赖jar包和一系列properties配置。场景可以绑定某一个环境,以便在压测过程中引用环境中jar包,而不用每次配置场景都上传依赖jar包。对环境的操作主要是增删改查。
1.4 其他压测配置
其他配置主要是PTS压测的配置,例如公网/VPC压测、并发量、引擎数量、压测时长等。
2 依赖
最新版本可以在maven公网地址查询。
<!--创建pts场景需要的实体类,如果只使用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.2</version>
<exclusions>
<exclusion>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>1.1.14</version>
</dependency>
<!--阿里云核心库。-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.2</version>
</dependency>
3 功能
PTS压测以场景为中心,借助PTS能力压测的jmeter也不例外。围绕jmeter场景,OpenAPI提供了如下几类功能:编辑场景、调试场景、压测场景、查看运行时数据、查看报告。
-
编辑场景:实现场景的增、删、改、查各种操作。
-
调试场景:实现场景的压测前调试。调试过程中可以通过压测数据接口获取调试结果数据,判断待压测API是否通,若不通过可提前停止调试。
-
压测场景:实现场景的启动、停止。调试成功后,可通过启动压测接口,实现一键启动压测,并在压测过程中,随时停止压测。
-
查看运行时数据:获取压测和调试过程中的实时数据。场景启动压测后,pts后端会5s上传一次数据,这个过程中可以实时看到压测执行情况,包括采样器实时采样的情况、压测任务所处阶段、执行压测引擎、实时消耗的vum等。
-
查看报告:查询压测最终报告数据。场景每次压测都会生成一个压测任务,同时生成一个压测任务对应的压测报告,压测报告中除了jmeter原生的日志外,还有pts针对某个采样器的成功率、TPS、RT指标的聚合数据。对报告的操作主要包括查看报告列表、查看jmeter原生日志以及pts对jmeter采样器压测指标的聚合数据。
4 使用
// 使用OpenAPI之前都需要构建一个client
Config config = new Config();
config.setAccessKeyId(accessKeyId);
config.setAccessKeySecret(accessKeySecret);
Client client = new Client(config);
4.1 编辑场景
创建/修改场景
SaveOpenJMeterSceneRequest request = new SaveOpenJMeterSceneRequest();
// 定义场景
SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterScene scene = new SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterScene();
// 设置引擎数量
scene.setAgentCount(2);
// 设置场景名
scene.setSceneName("test");
// 设置文件列表,包括jmeter脚本、jmeter压测依赖jar包、配置额度数据文件等
List<SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList> fileList = new ArrayList<>();
// 设置文件的属性 需要设置文件的名称和文件公网可访问的oss地址
SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList file = new SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList();
file.setFileName("json.jar");
file.setFileOssAddress("https://xx.oss-cn-shanghai.aliyuncs.com/json.jar");
fileList.add(file);
SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList testFile = new SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList();
testFile.setFileName("baidu.jmx");
testFile.setFileOssAddress("https://xx.oss-cn-shanghai.aliyuncs.com/baidu.jmx");
fileList.add(testFile);
scene.setFileList(fileList);
// 设置场景并发
scene.setConcurrency(1000);
// 设置压测持续时间
scene.setDuration(600);
// 设置测试文件的名称,这个文件需包括在文件列表中
scene.setTestFile("baidu.jmx");
// 若依赖了jmeter环境,课设置jmeter环境
scene.setEnviromentId("UKS1LH");
// 新建场景不需要设置id,修改场景需要设置id
scene.setSceneId("6FZQUTH");
request.setOpenJMeterScene(scene);
SaveOpenJMeterSceneResponse response = client.saveOpenJMeterScene(request);
查询场景
// 获取场景详细信息
GetOpenJMeterSceneRequest request = new GetOpenJMeterSceneRequest();
request.setSceneId("6FZQUTH");
GetOpenJMeterSceneResponse response = client.getOpenJMeterScene(request);
删除场景
RemoveOpenJMeterSceneRequest request = new RemoveOpenJMeterSceneRequest();
request.setSceneId("6FZQUTH");
RemoveOpenJMeterSceneResponse response = client.removeOpenJMeterScene(request);
4.2 调试场景
开始调试
StartDebuggingJMeterSceneRequest request = new StartDebuggingJMeterSceneRequest();
request.setSceneId("6FZQUTH");
StartDebuggingJMeterSceneResponse response = client.startDebuggingJMeterScene(request);
停止调试
StopDebuggingJMeterSceneRequest request = new StopDebuggingJMeterSceneRequest();
request.setSceneId("6FZQUTH");
StopDebuggingJMeterSceneResponse response = client.stopDebuggingJMeterScene(request);
4.3 压测场景
开始压测
StartTestingJMeterSceneRequest request = new StartTestingJMeterSceneRequest();
request.setSceneId("6FZQUTH");
StartTestingJMeterSceneResponse response = client.startTestingJMeterScene(request);
停止压测
StopTestingJMeterSceneRequest request = new StopTestingJMeterSceneRequest();
request.setSceneId("6FZQUTH");
StopTestingJMeterSceneResponse response = client.stopTestingJMeterScene(request);
4.4 查看运行时数据
GetJMeterSceneRunningDataRequest request = new GetJMeterSceneRunningDataRequest();
request.setSceneId("6FZQUTH");
GetJMeterSceneRunningDataResponse response = client.getJMeterSceneRunningData(request);
4.5 查看报告
报告列表
ListJMeterReportsRequest request = new ListJMeterReportsRequest();
// 分页设置
request.setPageNumber(1);
request.setPageSize(10);
// 查询条件设置
request.setSceneId("DYYPZIH");
request.setReportId("FLCAE382");
request.setKeyword("test");
request.setBeginTime(1637485804233L);
request.setEndTime(1637485804300L);
ListJMeterReportsResponse response = client.listJMeterReports(request);
采样器聚合数据
GetJMeterSampleMetricsRequest request = new GetJMeterSampleMetricsRequest();
// 设置报告id
request.setReportId("FLCAE382");
// 设置采样器的索引,为空或者<0时,赋值为-1返回全场景
request.setSamplerId(0);
request.setBeginTime(1637485804233L);
request.setEndTime(1637485804300L);
GetJMeterSampleMetricsResponse response = client.getJMeterSampleMetrics(request);
采样器采样日志
GetJMeterSamplingLogsRequest request = new GetJMeterSamplingLogsRequest();
// 分页设置
request.setPageNumber(1);
request.setPageSize(10);
// 条件设置
request.setReportId("FLCAE382");
request.setBeginTime(1637224899406L);
request.setEndTime(1637224976000L);
// 关键字
request.setKeyword("test");
// 采样器索引
request.setSamplerId(0);
// 结果是否成功
request.setSuccess(true);
// 线程
request.setThread("main");
// 响应码
request.setResponseCode("200");
// 最小响应时间
request.setMinRT(0);
// 最大响应时间
request.setMaxRT(1000);
// 压测引擎编号
request.setAgentId(14201002L);
GetJMeterSamplingLogsResponse response = client.getJMeterSamplingLogs(request);
压测机器运行日志
GetJMeterLogsRequest request = new GetJMeterLogsRequest();
// 分页设置
request.setPageNumber(1);
request.setPageSize(10);
// 查询的压测引擎索引
request.setAgentIndex(0);
request.setReportId("FLCAE382");
GetJMeterLogsResponse response = client.getJMeterLogs(request);
5 一键启动
压测的核心步骤:创建场景->压测场景->查看报告,以下代码实现了使用SDK一键启动压测场景,并且在完成压测后查看压测报告。具体步骤如下:
-
引入pom依赖
-
复制下列代码
-
填写自己的ak/sk
-
点击启动
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();
// 创建场景
String sceneId = createScene(client);
// 启动场景
String reportId = startTesting(client, sceneId);
// 最多等待次数
int count = 0;
// 查询是否已生成报告
while (!hasReport(client, reportId) && count++ < 20) {
// 若报告还未生成,则等待(30s)一段时间再查询
// 根据压测时间酌情等待
Thread.sleep(30 * 1000);
}
// 查看报告
getJMeterReport(client, reportId);
}
private static boolean hasReport(Client client, String reportId) throws Exception {
ListJMeterReportsRequest request = new ListJMeterReportsRequest();
// 分页设置
request.setPageNumber(1);
request.setPageSize(1);
// 查询条件设置
request.setReportId(reportId);
ListJMeterReportsResponse response = client.listJMeterReports(request);
return response.getBody().getReports().size() > 0;
}
private static void getJMeterReport(Client client, String reportId) throws Exception {
// 查看机器日志
GetJMeterLogsResponse getJMeterLogsResponse = getJMeterLogs(client, reportId);
List<Map<String, ?>> logs = getJMeterLogsResponse.getBody().getLogs();
// 查看采样器聚合数据
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();
// 分页设置
request.setPageNumber(1);
request.setPageSize(10);
// 条件设置
request.setReportId(reportId);
GetJMeterSamplingLogsResponse response = client.getJMeterSamplingLogs(request);
return response;
}
private static GetJMeterSampleMetricsResponse getJMeterSampleMetrics(Client client, String reportId) throws Exception {
GetJMeterSampleMetricsRequest request = new GetJMeterSampleMetricsRequest();
// 设置报告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();
// 分页设置
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();
// 设置场景名
scene.setSceneName("test");
// 设置文件列表,包括jmeter脚本、jmeter压测依赖jar包、配置额度数据文件等
List<SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList> fileList = new ArrayList<SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList>();
// 设置文件的属性 需要设置文件的名称和文件公网可访问的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);
// 设置场景并发
scene.setConcurrency(1000);
// 设置引擎数量 说明:一台引擎最多能发500并发,最少1并发所以此处能设置的引擎数为[2,1000],另外引擎数量越多消耗vum越快
scene.setAgentCount(2);
// 设置压测持续时间 60s
scene.setDuration(60);
// 设置测试文件的名称,这个文件需包括在文件列表中
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;
}
}