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

作者:灵苒、涧泉

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

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

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

  1. 场景配置。配置压测场景模拟用户(业务)与系统的交互。
  2. 压测执行。按指定压力量级启动压测。
  3. 压测监控分析。压测中通常关注施压 RPS,成功率,业务响应时间(RT),网络带宽等关键指标。
  4. 报告总结。披露系统能力是否符合要求,同时沉淀记录系统性能演变和优化过程。

原生 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 扩展目录下

点击压测

新建 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,成功率,响应时间,网络带宽等监控数据的变化趋势图。

业务 API 采样日志

很多时候我们还希望看到一个具体请求执行的详细信息。如有 1% 的请求失败,需要查看完整的请求、响应内容,以排查失败原因等。JMeter 图形界面下测试脚本时,可添加 View Results Tree 查看单个请求的详细信息,但执行压力测试时,对每个请求都记录详细信息,不仅没有必要,而且非常耗费资源,影响施压性能。

阿里云 PTS 采取了一个折中的办法,施压引擎间隔一段时间对每个业务 API(压测Sampler)分别采样记录一条成功和失败(如果有)的请求详细信息。在压测中或压测报告页面,点击查看采样日志按钮即可查询记录的请求采样信息,并支持按业务 API(压测Sampler),响应状态(是否成功),请求时间等进行搜索过滤。

点击查看详情即可看到单个请求的详细信息。目前对详细信息提供了通用和 HTTP 两种展示模板,HTTP 展示模板可针对 HTTP 请求进行更友好的排版展示,展示内容包括请求 URL,请求方法,返回码,完整的请求头、请求体,响应头、响应体等。

因为页面上只展示文本内容,请求体或响应体包含图片等无法识别为文本的内容时,可能显示为乱码。另外当请求体或响应体很大时,对应的内容可能被截断。

JMeter 日志

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

JMeter 日志主要用于脚本执行报错时排查错误原因。一些插件可能通过 JMeter 日志输出一些重要信息,用户在 groovy 脚本等代码中也可以直接打印日志。

报告总结

压测结束后,PTS 将汇总监控数据形成压测报告。用户根据压测报告分析评估系统性能是否符合要求,如 RPS,成功率和 RT(响应时间)是否符合期望。并可辅助用户排查分析业务系统性能瓶颈。

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

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

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

原文链接

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

阿里巴巴在开源压测工具 JMeter 上的实践和优化的更多相关文章

  1. HTTP 2.0 之压测工具 Jmeter

    年后,德国总部工业平台要做中国本地化,德国的同事过来给我们展示日志的时候,无意间看到了他们应用日志里的 HTTP/2,下意识到,原来他们都已经提供Http 2.0的服务了. 那么问题来了,除了h2lo ...

  2. 压测工具 jmeter入门教程及汉化修改

    Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件.相比Loadrunner而言,JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工 ...

  3. 压测工具-Jmeter

    server压力测试首选: Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域. 1 JMet ...

  4. super-smack压测工具

    简介 super-smack是一款开源压测工具,支持MySQL.PostgreSQL.Oracle.本篇主要介绍一下使用super-smack压测MySQL体会. 1.SQL定义 2.数据字典定义 3 ...

  5. 1. 堪比JMeter的.Net压测工具 - Crank 入门篇

    目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...

  6. 2. 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml

    目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...

  7. 3. 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识bombardier

    目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...

  8. 4. 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识wrk、wrk2

    目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...

  9. 5. 堪比JMeter的.Net压测工具 - Crank 实战篇 - 接口以及场景压测

    目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...

  10. 6. 堪比JMeter的.Net压测工具 - Crank 实战篇 - 收集诊断跟踪信息与如何分析瓶颈

    目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...

随机推荐

  1. netty Recycler对象池

    前言 池化思想在实际开发中有很多应用,指的是针对一些创建成本高,创建频繁的对象,用完不弃,将其缓存在对象池子里,下次使用时优先从池子里获取,如果获取到则可以直接使用,以此降低创建对象的开销. 我们最熟 ...

  2. python学习笔记(2): 函数式编程

    函数式编程 高阶函数 map() map接受一个函数和一个可迭代对象(Iterable),返回一个map对象(Iterator) def f(x): return x*x list(map(f, [1 ...

  3. 27_H.264解码实战

    目录 使用FFmpeg命令进行H.264解码 使用FFmpeg代码进行H.264解码 1.获取解码器 3.创建解析器上下文 4.创建AVPacket 5.创建AVFrame 6.打开解码器 7.打开文 ...

  4. ElasticSearch 准实时原理

    Elasticsearch 是一个基于 Lucene 库的搜索引擎.它提供了一个准实时的.分布式.支持多租户的全文搜索引擎. ----维基百科 那么问题来了,为啥 Elasticsearch 不是实时 ...

  5. Android 开发Day6

    <?xml version="1.0" encoding="UTF-8"?> <project version="4"&g ...

  6. django(cookie与session、中间件、auth模块)

    一 cookie与session 1 发展史及简介 """ 发展史 1.网站都没有保存用户功能的需求,所有用户访问返回的结果都是一样的 eg:新闻.博客.文章 2.出现了 ...

  7. Python简单程序设计(计算程序设计(存款利息)篇)

    如题: 解题方式如下:

  8. SqlServer查询表的所有字段属性及其是否是主外键

    CREATE PROC [dbo].[sp_help2] @TableName VARCHAR(50) = NULL AS SET NOCOUNT ON SET TRANSACTION ISOLATI ...

  9. nodejs下载 安装 配置环境

    1.下载 下载地址:https://nodejs.org/en/download/ 下载完成后,双击安装包,开始安装,一直点next即可.我把安装路径设置为 D:\Program Files\node ...

  10. hadoop集群查看所有主机的jps进程情况脚本文件

    jpsall代码 #!/bin/bash for host in hadoop102 hadoop103 hadoop104 do echo =============== $host ======= ...