当前位置: 首页 > 后端技术 > Java

阿里巴巴对开源压测工具JMeter的实践与优化

时间:2023-04-01 15:21:31 Java

介绍:ApacheJMeter是Apache下的一款开源压测工具。它创建于1999年初,至今已有20多年的历史。JMeter具有丰富的功能和庞大的社区(用户群),是主流的开源压力测量工具之一。作者:凌然、剑泉ApacheJMeter[1]是Apache下开源的一款压测工具。它创建于1999年初,至今已有20多年的历史。JMeter具有丰富的功能和庞大的社区(用户群),是主流的开源压力测量工具之一。性能测试通常针对新系统上线或大型活动(如电商促销、春节活动等),验证系统能力,帮助排查和定位性能瓶颈。压力测试大致分为几个步骤:场景配置。配置压测场景,模拟用户(服务)与系统的交互。执行压力测试。在规定的压力等级下开始压力测试。压力监测和分析。压力测试通常关注压力RPS、成功率、服务响应时间(RT)、网络带宽等关键指标。报告摘要。披露系统能力是否满足要求,同时记录系统性能演进和优化过程。原生JMeter实现压测在JMeterGUI页面编辑压测脚本,点击开始按钮调试JMeter脚本。具体操作请参考JMeter官网[1]。对于简单的场景和低测试并发,JMeter本地测试可以满足要求。但是随着互联网用户的增多,对系统更大并发量的需求与日俱增,而单台JMeter压压机的压压能力有一定的上限,因此需要使用多台压压机来进行提高JMeter的施压能力,需要使用JMeter的分布式施压功能。JMeter的分布式压测需要用户管理和维护多台机器。使用过程中注意以下几点:压机的防火墙已经关闭或者打开了正确的端口。为RMI设置或禁用SSL。所有印刷机都在同一个子网上。如果使用192.xxx或10.xxxIP地址,则服务器位于同一子网中。所有印刷机都使用相同版本的JMeter和Java。所有压机都拷贝了拆分后的CSV数据文件,依赖的jar包等,压测过程中需要监控压机是否正常出流,压力与配置保持一致。配置加压前监控数据的采集,方便压测后生成报表。可见JMeter的分布式压测需要协调各种资源,在前期准备和施压过程中维护压力引擎比较麻烦,对实施压测的人员来说压测效率低。云上JMeter实践阿里巴巴拥有非常丰富的业务形态,每一种业务形态背后都由一系列分布式技术系统提供,随着业务的快速发展,尤其是在双十一等促销活动中,准确评估整个业务站点的服务能力成为一大技术难题。在此过程中,我们构建了自己的全链路压测体系,以应对更加复杂多样的压测需求,并将该技术输出到性能测试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依赖com.aliyunpts-api-entity1.0.1com.aliyunpts202010201.8.10com.aliyunaliyun-java-sdk-core4.5.2复制以下代码importcom.aliyun.pts20201020.Client;importcom.aliyun.pts20201020.models.*;importcom.aliyun.teaopenapi.models.Config;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;publicclassStartingDemo{publicstaticvoidmain(String[]args)throwsException{Clientclient=getClient();//创建场景StringsceneId=createScene(client);//开始场景StringreportId=startTesting(client,sceneId);//最大等待次数intcount=0;//查询报表是否生成while(!hasReport(client,reportId)&&count++<20){//如果报表没有生成,则等待(30s)一段时间再查询//WaitThread.睡眠(30*1000);}//查看报告getJMeterReport(client,reportId);}私有静态布尔值nhasReport(Clientclient,StringreportId)抛出异常{ListJMeterReportsRequestrequest=newListJMeterReportsRequest();//分页设置request.setPageNumber(1);request.setPageSize(1);//查询条件设置request.setReportId(reportId);ListJMeterReportsResponse响应=client.listJMeterReports(请求);返回响应.getBody().getReports().size()>0;}privatestaticvoidgetJMeterReport(Clientclient,StringreportId)throwsException{//查看机器日志GetJMeterLogsResponseLogsResponseter=getJMeterLogs(client,reportId);List>logs=getJMeterLogsResponse.getBody().getLogs();//查看采样器聚合数据GetJMeterSampleMetricsResponsegetJMeterSampleMetrics=getJMeterSampleMetrics(client,reportId);ListsampleMetricList=getJMeterSampleMetrics.getBody().getSampleMetricList();//查看采样日志GetJMeterSamplingLogsResponsegetJMeterSamplingLogs=getJMeterSamplingLogs(client,reportId);ListsampleResults=getJMeterSamplingLogs.getBody().getSampleResults();}privatestaticGetJMeterSamplingLogsResponsegetJMeterSamplingLogs(Clientclient,StringreportId)throwsException{GetJMeterSamplingLogsRequestrequest=newGetJMeterSamplingLogsRequest();//分页设置request.setPageNumber(1);request.setPageSize(10);//条件设置request.setReportId(reportId);GetJMeterSamplingLogsResponse响应=client.getJMeterSamplingLogs(请求);返回响应;}privatestaticGetJMeterSampleMetricsResponsegetJMeterSampleMetrics(Clientclient,StringreportId)throwsException{GetJMeterSampleMetricsRequestrequest=newGetJMeterSampleMetricsRequest();//设置报告idrequest.setReportId(reportId);GetJMeterSampleMetricsResponse响应=client.getJMeterSampleMetrics(请求);返回响应;}privatestaticGetJMeterLogsResponsegetJMeterLogs(Clientclient,StringreportId)throwsException{GetJMeterLogsRequestrequest=newGetJMeterLogsRequest();//分页设置request.setPageNumber(1);request.setPageSize(10);//查询的压力测量引擎请求request.setReportId(reportId);GetJMeterLogsResponse响应=client.getJMeterLogs(请求);返回响应;}privatestaticStringstartTesting(Clientclient,StringsceneId)throwsException{StartTestingJMeterSceneResponsestartTestingSceneResponse=startTestingScene(client,sceneId);StringreportId=startTestingSceneResponse.getBody().getReportId();返回报告编号;}privatestaticStartTestingJMeterSceneResponsestartTestingScene(Clientclient,StringsceneId)throwsException{StartTestingJMeterSceneRequestrequest=newStartTestingJMeterSceneRequest();request.setSceneId(sceneId);StartTestingJMeterSceneResponseresponse=client.startTestingJMeterScene(request);返回响应;}privatestaticStringcreateScene(Clientclient)throwsException{SaveOpenJMeterSceneRequestrequest=newSaveOpenJMeterSceneRequest();//定义场景SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterScenescene=newSaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterScene();//设置场景名称scene.setSceneName("test");//设置文件列表,包括JMeter脚本、JMeter压测依赖jar包、配置quota数据文件等//设置文件的属性,需要设置文件名和文件公网可访问的oss地址SaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileListtestFile=newSaveOpenJMeterSceneRequest.SaveOpenJMeterSceneRequestOpenJMeterSceneFileList();testFile.setFileName("baidu.jmx");testFile.setFileOssAddress("https://pts-openapi-test.oss-cn-shanghai.aliyuncs.com/baidu.jmx");文件列表。添加(测试文件);scene.setFileList(文件列表);//设置场景并发数,可以设置为100万scene.setConcurrency(1000000);//设置引擎数说明:一个引擎最多可以发送500个并发,最少1个并发所以这里可以设置的引擎数是[2,1000],引擎数越多速度越快消耗vumscene.setAgentCount(2000);//设置压测时长为60sscene.setDuration(60);//设置测试文件该文件名需要包含在文件列表中scene.setTestFile("baidu.jmx");request.setOpenJMeterScene(场景);SaveOpenJMeterSceneResponse响应=client.saveOpenJMeterScene(请求);返回响应.getBody().getSceneId();}privatestaticClientgetClient()throwsException{//填写自己的AK/SKStringaccessKeyId="ak";StringaccessKeySecret="sk";配置配置=新配置();config.setAccessKeyId(accessKeyId);config.setAccessKeySecret(accessKeySecret);客户端客户端=新客户端(配置);回头客;点击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)都可以设置一个具有业务意义的名称(比如首页、下载页面),我们可以调用业务API。JMeter监控统计是按照业务API名称汇总的,比如两个同名的请求节点会汇总为一个业务API。配置脚本时需要注意不同的业务API节点要配置不同的名称。业务API压力负载和性能在实际工作中,不同业务API的统计数据可能存在巨大差异(例如浏览商品RT通常比提交订单快很多),因此PTS会展示每个业务的独立统计数据API默认(如上图压力测试页面显示的首页和下载页面)。后台记录压测中各时间点的数据PTS,最终形成完整直观的压测报告。点击业务API实时监控趋势图按钮,可以查看RPS、成功率、响应时间、网络带宽等相应监控数据的趋势图。image.gif业务API采样日志很多时候我们希望看到特定请求执行的详细信息。如果有1%的请求失败,则需要查看完整的请求和响应内容,排查失败原因。在JMeter图形界面下测试脚本时,可以添加ViewResultsTree来查看单个请求的详细信息,但是在进行压力测试时,记录每个请求的详细信息不仅没有必要,而且非常耗费资源,影响压力性能。阿里云PTS采用折中的方式。压力引擎每隔一段时间对每个业务API(压力测试采样器)采样并记录成功和失败(如果有)请求详情。在压测或压测报告页面,点击查看采样日志按钮可以查询记录的请求采样信息,支持按业务API(压测Sampler)、响应状态(成功)、请求时间等进行搜索过滤。单击查看详细信息以查看单个请求的详细信息。目前提供两种显示模板,general和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更易于使用,提供简单直观的监控,并提供海量压力能力。原文链接本文为阿里云原创内容,未经许可不得转载。