乐趣区

关于java:阿里巴巴在开源压测工具-JMeter-上的实践和优化

简介:Apache JMeter 是 Apach 旗下的开源压测工具,创立于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)宏大,是支流开源压测工具之一。

作者:灵苒、涧泉

Apache JMeter[1] 是 Apach 旗下的开源压测工具,创立于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)宏大,是支流开源压测工具之一。

性能测试通常集中在新零碎上线或大型流动前(如电商大促,春节流动等),以验证零碎能力,帮忙排查定位性能瓶颈等问题。

一次压测流动可粗略分为几个步骤:

  • 场景配置。配置压测场景模仿用户(业务)与零碎的交互。
  • 压测执行。按指定压力量级启动压测。
  • 压测监控剖析。压测中通常关注施压 RPS,成功率,业务响应工夫(RT),网络带宽等要害指标。
  • 报告总结。披露零碎能力是否符合要求,同时积淀记录零碎性能演变和优化过程。

原生 JMeter 施行压测

在 JMeter 的 GUI 页面编辑压测脚本,点击开始按钮调试 JMeter 脚本,具体操作可参考 JMeter 官网[1]。

对于场景简略,要求测试并发量不高的状况下,JMeter 本地测试就能满足需要。但随着互联网用户的减少,对系统承载更大并发的需要日渐晋升,而单台 JMeter 施压机的施压能力有肯定下限,所以须要应用多台施压机,以进步 JMeter 的施压能力,这就要应用到 JMeter 的分布式施压性能。

JMeter 的分布式压测须要用户本人治理保护多台机器,应用过程中留神以下几点:

  • 施压机的防火墙已敞开或关上了正确的端口。为 RMI 设置了 SSL 或禁用了它。
  • 所有施压机都在同一个子网上。如果应用 192.xxx 或 10.xxx IP 地址,则服务器位于同一子网中。
  • 所有施压机上应用雷同版本的 JMeter 和 Java。
  • 所有施压机都曾经拷贝了切分好的 CSV 数据文件、依赖 jar 包等。
  • 压测过程中须要监控施压机是否失常发流量,放弃压力与配置统一。
  • 施压前配置好监控数据的收集,不便压测完结后报告的生成。

由此可见 JMeter 的分布式压测须要协调各资源,前置筹备以及施压过程保护施压引擎比拟麻烦,对施行压测的人员来说压测效率低。

云上的 JMeter 实际

阿里巴巴有着十分丰盛的业务状态,每一种业务状态背地都由一系列分布式的技术体系提供服务,随着业务的疾速倒退,特地是在双 11 等大促营销等流动场景下,精确评估整个业务站点的服务能力成为一大技术难题。

在这个过程中,咱们打造了本人的全链路压测系统,以应答更简单、更多样的压测需要,并将此技术输入到 性能测试 PTS 上,同时反对原生 JMeter 压测。

通过控制台实际 JMeter

上传脚本

关上 PTS 控制台[2] 主页,左侧导航栏抉择压测核心 > 创立场景 > JMeter 压测,新建 JMeter 压测场景。填写场景名,如 jmeter-test。场景配置页面点击上传文件按钮,上传本地测试通过的 test.jmx 脚本。

施压配置

施压配置 页面,并发数设置为 50,压测时长设置为 2 分钟。

保留压测

点击保留去压测,弹出提示框点击确认,PTS 即开始在云端引擎执行 JMeter 脚本发动压力。

压测中页面如下:

留神:因为机器配置和网络环境的差别(PTS 施压机默认为 4 核 8G,BGP 多线路公网),PTS 上压测后果可能与本地压测后果存在肯定差别。另外,PTS 上的施压配置会笼罩原脚本中的配置,原脚本无论是写死固定配置还是应用 JMeter 属性配置都没关系。

通过 OpenAPI 实际 JMeter

云计算会倒退成像水电煤一样,成为社会的基础设施。OpenAPI 好比一条条疾速管道,连贯着企业和阿里云,把资源源源不断的输送给企业。应用云计算来构建 IT 基础设施是将来的发展趋势,这一点曾经成为社会共识。OpenAPI 是云服务凋谢的重要窗口,没有 OpenAPI 的云服务将很难被客户的零碎所集成,既影响了用户体验,也制约了云厂商自身的倒退。同样的,在压测畛域,随着压测需要日益多样化,更多用户心愿将云上的压测能力继承到本人的零碎,或者依据本人的业务零碎,编排自定义的压测平台,从而实现自动化定制化压测需要。

以下代码实现了应用 PTS 的 OpenAPI 一键启动 JMeter 压测场景,并且在实现压测后查看压测报告。

引入 pom 依赖

<!-- 创立 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.10</version>
</dependency>
<!-- 阿里云外围库。-->
<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>aliyun-java-sdk-core</artifactId>
  <version>4.5.2</version>
</dependency>

复制下列代码

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);
        // 设置场景并发,可设置为 100 万
        scene.setConcurrency(1000000);
        // 设置引擎数量 阐明:一台引擎最多能发 500 并发,起码 1 并发所以此处能设置的引擎数为[2,1000],另外引擎数量越多耗费 vum 越快
        scene.setAgentCount(2000);
        // 设置压测持续时间 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;
    }
}

填写本人的 ak/sk

在上述代码的 getClient 中填写正确的 ak/sk

点击启动

点击 main 办法启动

通过插件实际 JMeter

对于长期应用 JMeter 的用户来说,学习一款新的压测工具还是须要肯定的工夫老本。因而,PTS 开发了一款 PTS-JMeter 插件,可帮忙 JMeter 用户在不扭转原来的压测行为下间接应用 PTS 的压测资源。用户简直不感知 PTS-JMeter 插件的存在,与原生 JMeter 应用形式统一,保留 / 关上 JMeter 脚本点击启动压测即可。

下载安装

点击链接下载最新版本 jar 包[3]

将 jar 包拷贝到 JMeter 主目录下的 lib/ext 扩大目录下 image.gif

点击压测

新建 JMeter 脚本,或者关上已有 JMeter 脚本,点击 PTS-JMeter 启动按钮开始压测

查看报告

压测过程中,JMeter 图形界面会显示局部压测指标,用户可随时去控制台查看压测过程。压测完结后,PTS 会生成更加具体的压测报告,默认保留 30 天,用户可随时去控制台查看。

其余

PTS-JMeter 插件更具体的应用形式能够去 PTS 帮忙文档 [4] 中查看。

压测监控剖析

性能测试不仅仅是简略的发动压力,对压力负载(RPS,网络带宽等)和业务体现(RT,成功率等)的监控和剖析也是压测流动的重要组成部分。JMeter 脚本中每个申请节点(Sampler)可设置一个具备业务含意的名字(如 home 和 download page),咱们可称之为业务 API。JMeter 监控统计按业务 API 名字汇总,如两个名字雷同的申请节点将汇总统计为一个业务 API。配置脚本时需注意,不同业务 API 节点应配置为不同的名字。

业务 API 压力负载和体现

理论工作中,不同业务 API 的统计数据可能存在微小差别(如浏览商品 RT 通常比提交订单快很多),因而 PTS 默认将各个业务 API 独立统计展现(如上述压测中页面展现的 home 和 download page)。

压测中每个工夫点的数据 PTS 都在后盾记录了下来,最终将造成残缺直观的压测报告。点击业务 API 实时监控趋势图按钮,即可查看对应的 RPS,成功率,响应工夫,网络带宽等监控数据的变化趋势图。image.gif

业务 API 采样日志

很多时候咱们还心愿看到一个具体申请执行的详细信息。如有 1% 的申请失败,须要查看残缺的申请、响应内容,以排查失败起因等。JMeter 图形界面下测试脚本时,可增加 View Results Tree 查看单个申请的详细信息,但执行压力测试时,对每个申请都记录详细信息,不仅没有必要,而且十分消耗资源,影响施压性能。

阿里云 PTS 采取了一个折中的方法,施压引擎距离一段时间对每个业务 API(压测 Sampler)别离采样记录一条胜利和失败(如果有)的申请详细信息。在压测中或压测报告页面,点击查看采样日志按钮即可查问记录的申请采样信息,并反对按业务 API(压测 Sampler),响应状态(是否胜利),申请工夫等进行搜寻过滤。

点击查看详情即可看到单个申请的详细信息。目前对详细信息提供了通用和 HTTP 两种展现模板,HTTP 展现模板可针对 HTTP 申请进行更敌对的排版展现,展现内容包含申请 URL,申请办法,返回码,残缺的申请头、申请体,响应头、响应体等。

因为页面上只展现文本内容,申请体或响应体蕴含图片等无奈辨认为文本的内容时,可能显示为乱码。另外当申请体或响应体很大时,对应的内容可能被截断。

JMeter 日志

本地执行 JMeter 脚本时,默认将日志记录到 jmeter.log 文件。在 PTS 上执行 JMeter 脚本时,可通过 JMeter 日志页面实时查看 JMeter 日志,并反对依据日志级别、工夫或线程名进行查问过滤。

image.gifJMeter 日志次要用于脚本执行报错时排查谬误起因。一些插件可能通过 JMeter 日志输入一些重要信息,用户在 groovy 脚本等代码中也能够间接打印日志。

报告总结

压测完结后,PTS 将汇总监控数据造成压测报告。用户依据压测报告剖析评估零碎性能是否符合要求,如 RPS,成功率和 RT(响应工夫)是否合乎冀望。并可辅助用户排查剖析业务零碎性能瓶颈。

PTS 压测报告页面可查问历史压测报告列表。

点击查看报告关上查看报告详情。压测报告在 PTS 上默认保留 30 天,可点击报告导出按钮,导出保留 PDF 版压测报告到本地。压测报告概要信息包含压测执行工夫,RPS,RT,成功率等概要数据。场景详情蕴含全场景维度和业务 API 维度的监控统计信息。

相比手动命令行执行 JMeter 脚本,PTS 更加简略易用,提供简略直观的监控,并提供海量施压能力。

原文链接
本文为阿里云原创内容,未经容许不得转载。

退出移动版