你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

适用于 Java 的 Azure 负载测试客户端库 - 版本 1.0.7

Azure 负载测试向用户提供 Java 中的客户端库,用户可以通过该库与 Azure 负载测试服务进行本机交互。 Azure 负载测试是一项完全托管的负载测试服务,可用于生成大规模负载。 该服务可以模拟应用程序的流量,且无需其托管位置。 开发人员、测试人员和质量保证 (QA) 工程师可以使用它来优化应用程序性能、可伸缩性或容量

此包包含 Microsoft Azure 开发人员负载测试客户端库。

文档

提供了各种文档来帮助你入门

入门

先决条件

将包添加到产品

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-developer-loadtesting</artifactId>
    <version>1.0.7</version>
</dependency>

Authentication

Azure 标识 包提供用于对客户端进行身份验证的默认实现。

默认情况下,Azure Active Directory 令牌身份验证依赖于以下环境变量的正确配置。

  • AZURE_CLIENT_ID 用于 Azure 客户端 ID。
  • AZURE_TENANT_ID 用于 Azure 租户 ID。
  • AZURE_CLIENT_SECRETAZURE_CLIENT_CERTIFICATE_PATH ,用于客户端密码或客户端证书。

此外,可以通过环境变量 AZURE_SUBSCRIPTION_ID配置 Azure 订阅 ID。

使用上述配置, azure 可以通过以下代码对客户端进行身份验证:

// ensure the user, service principal or managed identity used has Loadtesting Contributor role for the resource
TokenCredential credential = new DefaultAzureCredentialBuilder()
    .build();
// create client using DefaultAzureCredential
LoadTestAdministrationClient adminClient = new LoadTestAdministrationClientBuilder()
        .credential(credential)
        .endpoint("<Enter Azure Load Testing Data-Plane URL>")
        .buildClient();
LoadTestRunClient testRunClient = new LoadTestRunClientBuilder()
        .credential(credential)
        .endpoint("<Enter Azure Load Testing Data-Plane URL>")
        .buildClient();

RequestOptions reqOpts = new RequestOptions()
    .addQueryParam("orderBy", "lastModifiedDateTime")
    .addQueryParam("maxPageSize", "10");
adminClient.listTests(reqOpts);

reqOpts = new RequestOptions()
    .addQueryParam("orderBy", "lastModifiedDateTime")
    .addQueryParam("status", "EXECUTING,DONE")
    .addQueryParam("maxPageSize", "10");
testRunClient.listTestRuns(reqOpts);

关键概念

以下组件构成 Azure 负载测试服务。 使用适用于 Java 的 Azure 负载测试客户端库,可以使用客户端与其中每个组件进行交互。 有两个顶级客户端,它们是库main入口点

  • LoadTestingClient

  • LoadTestingAsyncClient

这两个客户端中具有类似的方法,但异步客户端中的方法也是异步的。

顶级客户端有两个子客户端

  • LoadTestAdministration

  • TestRun

这些子客户端用于管理和使用服务的不同组件。

负载测试管理客户端

LoadTestAdministration 客户端用于管理和配置负载测试、应用组件和指标。

测试

测试指定测试脚本和配置设置用于运行负载测试。 可以在 Azure 负载测试资源中创建一个或多个测试。

应用组件

为 Azure 托管应用程序运行负载测试时,可以监视不同 Azure 应用程序组件的资源指标(服务器端指标)。 负载测试运行时以及测试完成后,可以在 Azure 负载测试仪表板中监视和分析资源指标。

指标

在负载测试期间,Azure 负载测试收集有关测试执行的指标。 有两种类型的指标:

  1. 客户端指标会显示测试引擎报告的详细信息。 这些指标包括虚拟用户数、请求响应时间、失败的请求数或每秒请求数。

  2. 服务器端指标适用于 Azure 托管应用程序并显示有关 Azure 应用程序组件的信息。 指标可以是数据库读取次数、HTTP 响应的类型或容器资源消耗量。

测试运行客户端

TestRun 客户端用于启动和停止与负载测试对应的测试运行。 测试运行表示负载测试的一次执行。 它收集与运行 Apache JMeter 脚本关联的日志、负载测试 YAML 配置、要监视的应用组件列表以及测试结果。

Data-Plane 终结点

Azure 负载测试资源的数据平面可使用以下 URL 格式进行寻址:

00000000-0000-0000-0000-000000000000.aaa.cnt-prod.loadtesting.azure.com

第一个 GUID 00000000-0000-0000-0000-000000000000 是用于访问 Azure 负载测试资源的唯一标识符。 接下来 aaa 是资源的 Azure 区域。

数据平面终结点是从控制平面 API 获取的。

示例:1234abcd-12ab-12ab-12ab-123456abcdef.eus.cnt-prod.loadtesting.azure.com

在上面的示例中, eus 表示 Azure 区域 East US

示例

创建负载测试

LoadTestAdministrationClient adminClient = new LoadTestAdministrationClientBuilder()
        .credential(new DefaultAzureCredentialBuilder().build())
        .endpoint("<endpoint>")
        .buildClient();

// construct Test object using nested String:Object Maps
Map<String, Object> testMap = new HashMap<String, Object>();
testMap.put("displayName", "Sample Display Name");
testMap.put("description", "Sample Description");

// loadTestConfig describes the number of test engines to generate load
Map<String, Object> loadTestConfigMap = new HashMap<String, Object>();
loadTestConfigMap.put("engineInstances", 1);
testMap.put("loadTestConfiguration", loadTestConfigMap);

// environmentVariables are plain-text data passed to test engines
Map<String, Object> envVarMap = new HashMap<String, Object>();
envVarMap.put("a", "b");
envVarMap.put("x", "y");
testMap.put("environmentVariables", envVarMap);

// secrets are secure data sent using Azure Key Vault
Map<String, Object> secretMap = new HashMap<String, Object>();
Map<String, Object> sampleSecretMap = new HashMap<String, Object>();
sampleSecretMap.put("value", "https://samplevault.vault.azure.net/secrets/samplesecret/f113f91fd4c44a368049849c164db827");
sampleSecretMap.put("type", "AKV_SECRET_URI");
secretMap.put("sampleSecret", sampleSecretMap);
testMap.put("secrets", secretMap);

// passFailCriteria define the conditions to conclude the test as success
Map<String, Object> passFailMap = new HashMap<String, Object>();
Map<String, Object> passFailMetrics = new HashMap<String, Object>();
Map<String, Object> samplePassFailMetric = new HashMap<String, Object>();
samplePassFailMetric.put("clientmetric", "response_time_ms");
samplePassFailMetric.put("aggregate", "percentage");
samplePassFailMetric.put("condition", ">");
samplePassFailMetric.put("value", "20");
samplePassFailMetric.put("action", "continue");
passFailMetrics.put("fefd759d-7fe8-4f83-8b6d-aeebe0f491fe", samplePassFailMetric);
passFailMap.put("passFailMetrics", passFailMetrics);
testMap.put("passFailCriteria", passFailMap);

// convert the object Map to JSON BinaryData
BinaryData test = BinaryData.fromObject(testMap);

// receive response with BinaryData content
Response<BinaryData> testOutResponse = adminClient.createOrUpdateTestWithResponse("test12345", test, null);
System.out.println(testOutResponse.getValue().toString());

将 .jmx 文件上传到负载测试

LoadTestAdministrationClient adminClient = new LoadTestAdministrationClientBuilder()
    .credential(new DefaultAzureCredentialBuilder().build())
    .endpoint("<endpoint>")
    .buildClient();

// extract file contents to BinaryData
BinaryData fileData = BinaryData.fromFile(new File("path/to/file").toPath());

// receive response with BinaryData content
Response<BinaryData> fileUrlOut = adminClient.uploadTestFileWithResponse("test12345", "sample-file.jmx", fileData, null);
System.out.println(fileUrlOut.getValue().toString());

运行负载测试

LoadTestRunClient testRunClient = new LoadTestRunClientBuilder()
    .credential(new DefaultAzureCredentialBuilder().build())
    .endpoint("<endpoint>")
    .buildClient();

// construct Test Run object using nested String:Object Maps
Map<String, Object> testRunMap = new HashMap<String, Object>();
testRunMap.put("testId", "test12345");
testRunMap.put("displayName", "SDK-Created-TestRun");

// convert the object Map to JSON BinaryData
BinaryData testRun = BinaryData.fromObject(testRunMap);

// start test with poller
SyncPoller<BinaryData, BinaryData> poller = testRunClient.beginTestRun("testrun12345", testRun, null);
Duration pollInterval = Duration.ofSeconds(5);
poller = poller.setPollInterval(pollInterval);

// wait for test to reach terminal state
JsonNode testRunJson = null;
String testStatus;
PollResponse<BinaryData> pollResponse = poller.poll();
while (pollResponse.getStatus() == LongRunningOperationStatus.IN_PROGRESS || pollResponse.getStatus() == LongRunningOperationStatus.NOT_STARTED) {
    try {
        testRunJson = new ObjectMapper().readTree(pollResponse.getValue().toString());
        testStatus = testRunJson.get("status").asText();
        System.out.println("Test run status: " + testStatus);
    } catch (JsonProcessingException e) {
        System.out.println("Error processing JSON response");
        // handle error condition
    }

    // wait and check test status every 5 seconds
    try {
        Thread.sleep(pollInterval.toMillis());
    } catch (InterruptedException e) {
        // handle interruption
    }

    pollResponse = poller.poll();
}

poller.waitForCompletion();
BinaryData testRunBinary = poller.getFinalResult();
try {
    testRunJson = new ObjectMapper().readTree(testRunBinary.toString());
    testStatus = testRunJson.get("status").asText();
} catch (JsonProcessingException e) {
    System.out.println("Error processing JSON response");
    // handle error condition
}

String startDateTime = testRunJson.get("startDateTime").asText();
String endDateTime = testRunJson.get("endDateTime").asText();

// get list of all metric namespaces and pick the first one
Response<BinaryData> metricNamespacesOut = testRunClient.getMetricNamespacesWithResponse("testrun12345", null);
String metricNamespace = null;
// parse JSON and read first value
try {
    JsonNode metricNamespacesJson = new ObjectMapper().readTree(metricNamespacesOut.getValue().toString());
    metricNamespace = metricNamespacesJson.get("value").get(0).get("metricNamespaceName").asText();
} catch (JsonProcessingException e) {
    System.out.println("Error processing JSON response");
    // handle error condition
}

// get list of all metric definitions and pick the first one
Response<BinaryData> metricDefinitionsOut = testRunClient.getMetricDefinitionsWithResponse("testrun12345", metricNamespace, null);
String metricName = null;
// parse JSON and read first value
try {
    JsonNode metricDefinitionsJson = new ObjectMapper().readTree(metricDefinitionsOut.getValue().toString());
    metricName = metricDefinitionsJson.get("value").get(0).get("name").get("value").asText();
} catch (JsonProcessingException e) {
    System.out.println("Error processing JSON response");
    // handle error condition
}

// fetch client metrics using metric namespace and metric name
PagedIterable<BinaryData> clientMetricsOut = testRunClient.listMetrics("testrun12345", metricName, metricNamespace, startDateTime + '/' + endDateTime, null);
clientMetricsOut.forEach((clientMetric) -> {
    System.out.println(clientMetric.toString());
});

疑难解答

适用于 Java 的 Azure SDK 提供一致的日志记录案例,可帮助排查应用程序错误并加快解决。 生成的日志会在到达终端状态之前捕获应用程序的流,以帮助查找根本问题。 查看 日志记录 Wiki,获取有关启用日志记录的指南。

后续步骤

可在 SDK 的 GitHub 存储库中获取 Azure 加载测试 Java SDK 示例。 这些示例为经常遇到的其他方案提供了示例代码。 请参阅 Azure 负载测试示例

贡献

有关参与此存储库的详细信息,请参阅 参与指南

  1. 分支
  2. 创建功能分支 (git checkout -b my-new-feature)
  3. () git commit -am 'Add some feature' 提交更改
  4. 推送到分支 (git push origin my-new-feature)
  5. 创建新的拉取请求