1分钟构建API网关日志解决方案
访问日志(Acccess Log)是由web服务生成的日志,每一次api请求都对应一条访问记录,内容包括调用者IP、请求的URL、响应延迟、返回状态码、请求和响应字节数等重要信息。
阿里云API网关提供API托管服务,在微服务聚合、前后端分离、系统集成上为用户提供诸多便利。
访问日志对于API网关的意义尤为重要,它可以帮助使用者打破黑盒,了解其web服务的运行状况。但实际上,云服务厂商为其用户提供访问日志确实存在不小的挑战:
日志分发到用户空间的实时性:从用户访问服务产生日志到日志对用户可见,业界不少的方案是“T+1”时效的;做到秒级延迟对于用户有非常的意义,使得异常告警、快速问题修复成为可能。
日志的ad-hoc查询能力:对于trouble shooting场景,工程师在个人经验和业务知识的基础上,不断缩小问题范围以致最终定位解决,这个过程中对于日志的分析和查询需求是不断变化的,即时查询的价值由此得到体现。
海量日志的快速分析能力:访问日志和请求PV是对应的,拥有百万级用户的服务每天甚至可以达到TB级别的访问日志规模,秒级延迟下满足常见的日志分析场景是一个重要能力。
在今天,API网关新支持了日志功能,基于日志服务为用户提供实时、自助分析访问日志的能力。
提到功能你可能会关心日志分析的费用,在这一点上,日志服务本身的价格对比业界方案(例如ELK)优势非常明显,且提供每月500MB额度的Free Tier,大部分用户只需要花很少的钱就可以用起来。
访问日志可以搞些什么事情
开通API网关日志功能后,你可以在日志服务上实时获取所有访问日志记录。日志服务对于访问日志的常见使用场景都提供了支持:
报表分析:通过ad-hoc分析语法实时计算结果,并对结果提供丰富的可视化展示
实时查询:业界有竞争力的访问日志查询体验,用户API被调用后可以秒级(1分钟,90%情况下15秒内)延迟获取对应的请求日志,查询语法支持全文关键词、Key-Value、Range查询,可以在1秒内处理千万级别日志
异常告警:借助分析语法对日志内容做个性化统计,对于统计结果支持阈值报警,对接阿里云通知中心、短信、钉钉进行告警
日志投递:一键配置数据投递OSS(低成本归档、数仓计算)、MaxCompute(数仓计算)等云产品,全托管运行
其它:更多个性化需求,可以通过函数计算、消费组、EMR等方式处理访问日志
阿里云API网关的访问日志格式如下:
字段名
字段含义
requestId
每次请求的唯一ID
apiGroupUid
分组ID
apiGroupName
分组名称
apiStageUid
环境ID
apiStageName
环境名称
apiUid
API的唯一ID
apiName
API名称
clientIp
调用者IP
appId
调用者的应用ID
appName
调用者应用名称
domain
请求的域名
httpMethod
请求方法
path
请求path
statusCode
HTTP状态码
errorMessage
错误信息
exception
异常信息
providerAliUid
API提供者阿里云ID
region
regionId
requestHandleTime
请求处理时间
requestSize
请求大小
responseSize
响应大小
serviceLatancy
服务端延时(单位毫秒)
玩转API网关访问日志
本节为大家介绍在日志服务控制台上如何对API网关访问日志做分析。
1. 报表分析
基于灵活的分析语法,你可以按照自己的思路写出分析语句,通过ad-hoc查询完成业务需求。日志服务对计算结果提供多种可视化展示方式,dashboard内每个图表的统计指标支持即时修改生效。
日志服务默认提供了一些API网关的分析报表如下:
api请求成功率、后端服务失败占比
请求PV最高的app、请求出现错误的api统计
访问来源app数、top请求次数的appGroup
api请求PV、服务延迟统计
top延时的api统计、top请求次数的api统计
除了官方提供的报表以外,你还可以自己写分析语句生成报表,例如,统计clientIp的访问来源的城市分布。
在日志Logstore查询框中填写分析语句:
* | select ip_to_city(clientIp) as client_ip_city, count(*) as request_count group by client_ip_city order by request_count desc limit 20
保存查询结果并添加到仪表盘:
在仪表盘中看到效果如下:
推荐10分钟精通Nginx访问日志分析统计给大家,这篇文章提供了一些有趣的访问日志分析样例可供参考。
2. 实时查询
快速问题诊断是一项非常重要的能力,可以帮助线上服务减少异常带来的损失。区别于业界常见的“T+1”日志投送能力,我们可以在日志服务上准实时获取到最新访问日志,为短时间内定位问题根源提供可能。
在收到告警或看到异常指标后,日志服务的大数据查询能力可以帮助你快速找到到值得关注的日志记录。例如,我们排查非getName api,GET请求且状态码为500、501、503的访问日志记录:
3. 监控报警
我们在日志服务上保存一个查询语句,计算api请求中非200 pv在总体请求pv的占比:
* | select sum(case when statusCode > 200 then 1 else 0 end) *1.0 / count(1) as non_200_ratio
按照报警设置指南将查询语句保存为告警,查询区间为最近10分钟,当非200 statusCode的请求比率超过千分之一时,通过钉钉发出告警:
4. 数据投递
日志服务支持1~365天的数据存储,假如你的日志规模很大,对于冷数据希望有更经济、更长时间的存储,可以通过配置OSS投递规则将数据投递到OSS存储。
怎样开通日志功能
你可以在日志服务、API网关两个产品的控制台上任选其一开通日志功能。
1. 日志服务控制台上开通步骤
a. 创建Logstore
若Logstore已存在请跳过本步骤。
b. 进入向导
若Logstore是第一步新建出来的:
如果Logstore是之前已存在的:
c. 选择日志源
d. 授权
e. 确认索引配置
在后的dashboard中将使用到这里列出的索引配置,请谨慎修改。
f. 保存
投递与ETL配置可暂不设置,在将来需要时再创建。
至此,wizard初始化工作完成,你可以选择刚才设置的Logstore:api-gateway-access-log进行日志查询、分析,或者进入仪表盘查看报表。
2. API网关控制台上开通步骤
在上图步骤之后,你的API网关访问日志可以分发到指定Logstore。
如果需要对Logstore做进一步分析,请参考“日志服务控制台上开通步骤”中b~f完成后续配置工作。
48 张图 | 手摸手教你微服务的性能监控、压测和调优
48 张图 | 手摸手教你微服务的性能监控、压测和调优本文基于我的开源项目 PassJava 地址:https://github.com/Jackson0714/PassJava-Platform本文已收录至:www.passjava.cn一、何为压力测试1.1、 大白话解释性能压测是什么:就是考察当前软件和硬件环境下,系统所能承受的最大负荷,并帮助找出系统的瓶颈所在。性能压测的目的:为了系统在线上的处理能力和稳定性维持在一个标准范围内,做到知己知彼,百战不殆。还可以发现内存泄漏、并发与同步的问题。1.2、性能指标RepsonseTime - RT:响应时间,用户从客户端发起一个请求开始计算,到客户端接收到服务端的响应结束,整个过程所耗费的时间。Hits Per Second - HPS:用户每秒点击次数,也就是每秒向后台发送的请求次数。QPS:系统每秒内处理查询的次数。MaxRT:最大响应时间,指用户发出请求到服务端返回响应的最大时间。MiniRT:最少响应时间,指用户发出请求到服务端返回响应的最少时间。90%响应时间:将所有用户的响应时间进行升序排序,取 90 % 的位置。性能测试关注点:吞吐量:每秒钟系统能处理的请求数、任务数。响应时间:服务处理一个请求或一个任务的耗时。错误率:一批请求中结果出过错的请求所占比例。二、Jmeter 压测工具1、Jmeter 工具下载和安装 Jmeter 工具下载地址:https://jmeter.apache.org/download_jmeter.cgi
我下载的版本是 apache-jmeter-5.3运行 JMeter 程序打开批处理文件:\apache-jmeter-5.3\bin\jmeter.bat添加线程组1s 内启动 200 个线程,循环次数 100 次。2w 个请求。测试 HTTP 请求配置要测试的协议、服务器地址、端口号协议:http服务器名称或 IP: www.baidu.com (只是为了演示)端口号:80添加察看结果树、汇总报告和聚合报告开始压力测试点击播放按钮就开始启动了。注意启动之前需要先设置线程组的参数配置和 HTTP 请求的配置。查看每个请求结果查看汇总报告主要关心平均值和吞吐量。200 个线程,每个线程调用 100 次,总共 2 w 次,可以看到下图中表格中的样本列也是 2 w,请求所耗费的时间是 151 ms,吞吐量是 880 个请求每秒。查看聚合报告主要看中位数和90%百分位,如下图所示,中位数是 59 ms,说明大部分请求的响应时间是 59 ms。90 % 的请求 都是在 271 ms 以内响应完成的。异常 0.41% 说明 2 w 个请求中有 82 个请求异常(20000 * 0.0041 = 82 )。吞吐量 880.2/sec 说明百度这个网站每秒能处理 880 个请求。性能还算可以。查看汇总图查看汇总图时,需要先勾选想要查看的信息然后查看图形汇总:可以看到勾选的几列在图表中是用不同颜色表示的,比如绿色的柱状条就是 90 % 百分位。我们来测试下 佳必过的管理后台的性能,吞吐量接近 2000/s。三、性能监控之 jconsolejconsole 和 jvisualvm 是 Java JDK 的两个小工具,用来监控内存泄漏、跟踪垃圾回收、执行时的内存情况、对 CPU 进行分析、线程的分析。都可以通过命令行启动,而且可以监控本地和远程应用。而 jvisualvm 是升级版的 jconsole。我们先来看下 jconsole 的使用。首先用 cmd 命令行的方式启动 jconsole。启动 jconsole选择监控哪个应用然后选择 passjava 项目的 question 服务。对应的就是下面这个微服务: passjava-question概览从监控界面上有 6 个菜单,首先看到的是概览功能,上面有堆内存使用量、线程数、类的使用情况、CPU 占用率,都是用趋势图来表示的,能很方便的看出当前性能的概览。注意:这些监控都是实时的。内存下面是内存的使用情况,可以从下图中看到有个下拉框,里面可以选择不同的内存维度,然后下面的图标和柱状图也会跟着选择的维度而展示不同。内存下面是线程的使用情况,可以看到线程峰值和活动线程的总数量,目前看到的峰值是59,活动线程数是 57。下半部分可以看到具体是哪些线程,以及线程的堆栈信息,非常详细。类下面是类的加载和卸载情况,已加载类总数是 10679,而已卸载的类是 1 个,所以当前已加装当前类的总数是 10679 - 1 = 10678 个。VM 概要我们再来看下VM(虚拟机)的情况。如下图所示,可以看到虚拟机情况,线程、类、堆的概要信息,以及 VM 的参数,是不是很方便呀~MBean 信息接下来我们来看下 MBean 信息。对于 MBean,可能很多同学不知道是啥,下面做个解释:MBean就是一种规范的JavaBean,通过集成和实现一套标准的Bean接口,这种叫MBean。MBean可以用来干嘛?就是可以有一套JDK级别的对外的服务接口。比如,你写了一个JVM允许状态辅助查询的Bean,你希望别人下载一个Jconsole就可以看到你写的杰作。那你就可以考虑用MBean规范来实现。很多垃圾收集器算法Bean就这么干的(说的就是这个类sun.management.MemoryImpl)。四、性能监控之 jvisualvmjvisualvm 比 jconsole 更强大,界面展示的信息更丰富。启动 jvisualvm 和概述启动方式和 jconsole 一样,也是通过 cmd 命令行启动。还是选择 passjava-question 微服务,然后选择第一个菜单栏:概述。可以看到 JVM 的版本,启动参数等信息。监视监视 CPU、堆、类、线程的情况。整体显示的效果比 jconsole 更美观。线程再来查下线程的情况。可以看到有 5 种状态的线程:运行:正在运行的线程。休眠:休眠状态的线程。等待:等待执行的线程。驻留:线程里面的空闲线程。监视:阻塞的线程,正在等待锁。抽样器另外我们也可以抽样器对 CPU 或内存进行抽样。如下图所示,对内存进行抽样。插件的使用安装Visual GC 插件安装步骤:工具->插件->可用插件->Visual GC->安装。安装完成后,重启就可以使用插件功能了。安装完成后,就可以看到下图是实时监控垃圾回收的情况。五、对网关的性能测试现在我想对 Passjava 系统的 question 微服务的接口进行一个压测,该如何进行呢?首先我们来看下 passjava 的架构是怎么样的,如下图所示:客户端分为手机端和 PC 端,http 请求先经过 API Gateway,然后再转发到 question 微服务。其中涉及到了中间件:Gateway 网关。我们来对 Gateway 网关进行压力测试。网关的端口号是 8060,我们配置下 JMeter。如下图所示:配置每秒发送 200 个请求,一直循环执行,直到手动停止压测。如下图所示:可以看下执行结果,吞吐量在 2422 个每秒,还是比较高的。吞吐量:2422/s 。90% 响应时间:142 毫秒。99% 响应时间:281 毫秒。我们再来看看垃圾回收的情况,Eden 区垃圾回收用时 2.7 s,用时太长了吧,看看这里怎么能优化下。通常的优化方向是增大新生代堆内存配置。六、对微服务的性能测试根据上面的架构原理图,我们知道客户端请求都是经过 Gateway 转发了一次的,如果我们想单独看下微服务的性能该怎么测试呢?下面我来演示下如何测试 passjava-question 微服务的性能。首先需要在 passjava-question 微服务中添加一个测试方法:有两种方式测试这个 api 是否添加正确。第一种用 postman 测试下这个请求是否能正确响应,返回 “test” 则表示响应正确。第二种通过浏览器进行测试。浏览器地址栏输入以下链接后,回车,看下浏览器窗口是否显示 “test”,是则表示响应正确。然后我们需要用 Jmeter 压测工具来测试这个微服务下的 api 的性能究竟如何。吞吐量:3542/s 。90% 响应时间:100 毫秒。99% 响应时间:152 毫秒。七、对网关+微服务的性能测试如果我们想对这个整个请求链路进行性能测试该怎么做?首先请求需要先经过网关,然后由网关转发到微服务。在之前的文章中,我已经将网关配置好了,所以要想通过网关转发到 test 请求,只需要对请求路径稍作修改即可,如下所示:http://localhost:8060/api/question/v1/admin/question/test然后在浏览器输入该网址,返回 “test” 即表示响应正确。然后我们还是用 Jmeter 压测工具测试下 test api 的性能。测试结果如下图所示:从结果可以看到:吞吐量:982/s 。90% 响应时间:437 毫秒。99% 响应时间:790毫秒。这里做个横向对比:说明微服务 api 经过网关转发一次后,性能至少下降了一半。可以得出结果:中间件越多,性能损失越大,大部分损失都是网络交互导致的。可以通过增强网络通信质量来减少网络的延迟。八、对数据库查询进行优化一般情况下,出现性能问题更多的是业务中查询数据库的耗时。接下来看下如何优化数据的查询。下面是一个查询问题列表的 api:通过问题类型 type 字段过滤问题列表。api 路径如下:http://localhost:11000/question/v1/admin/question/list?type=5这个 api 的代码如下,很容易看懂。我们加些测试代码:统计查询数据库的耗时。如下所示:然后重启 passjava-question 服务,再次测试这个 api,耗时 43 ms怎么对查询进行优化呢?很容易想到加索引,我们来试下加在 question 表加索引后的效果。给 type 字段加上普通索引,如下图所示:我们再来看下加了索引后的耗时情况:耗时 18 ms,确实比之前的 43 ms 快了很多。九、优化垃圾回收我们可以通过 jvisulavm工具查看垃圾回收的情况,Eden 区频繁发生 GC,短时间(1分钟)内共造成了 480 次 stop the world。另外从压测工具中也可以看到,吞吐量为 275/s。原因是 Eden 区的内存分配得太小了,只有 32 M,我们来调大一点。增大 Eden 区大小通过在 IDEA 工具中配置以下参数,调整堆内存最大为 1024 M,新生代内存为 512 M。-Xmx1024m -Xms1024m -Xmn512m然后可以观察到在短时间(1分钟)内只进行了 92 次垃圾回收,说明垃圾回收的频率降低了。应用程序的性能也提升了。另外从压测工具中也可以看到,吞吐量为 347/s,吞吐量也有较大提升。十、总结本文通过压测工具 Jmeter 讲解压测如何实施,然后用性能监控工具 jconsole 和 jvisualvm 来监控 Java 应用程序的性能,以及如何用工具来优化开源项目 passjava 的性能,并且非常详细地介绍了每一步以及执行结果,通过对比的方式,更加清晰地知道如何做性能优化。下面是对系统性能的常规优化手段:中间件较多时,优化网络通信质量。数据库查询耗时时,需要对查询进行优化,比如添加索引。模板的渲染速度,可以通过设置模板缓存。静态资源的获取,可以通过 Nginx 动静分离来解决。(下期再讲)日志太多,需要减少不必要的打 log 操作。巨人的肩膀:https://blog.csdn.net/u010833547/article/details/92806510https://www.bilibili.com/video/BV1np4y1C7Yfhttps://github.com/Jackson0714/PassJava-Platformwww.passjava.cn
OpenTSDB 生产应用与思考
作者:陈杰,欢聚时代YY 基础架构部,数据库技术组,专注于HBase、Kafka,MySQL 等技术。
OpenTSDB 官方介绍
http://opentsdb.net/overview.html
这里就不翻译了。
OpenTSDB 应用场景与数据量级
现在的时间序列数据库不仅仅可以提供原始数据的查询,而且要支持对原始数据的聚合能力,支持过滤、过滤之后的聚合计算,这些功能OpenTSDB 都有。架构设计也好,中间件也好,还是数据库,在实际生产场景中,都绕不开数据量级的考虑。OpenTSDB 在数据量小时是可用的,在千万级、亿级中提取几万条数据,比如某个指标半年内的5分钟级别的数据,还是很快响应的。但如果再提取多点数据,几十万,百万这样的量级,又或者提取后再做个聚合运算,OpenTSDB 就勉为其难啦,原因有几点:
【1】OpenTSDB 目前还是单点做聚合运算,我所知道的大的云商如阿里云HiTSDB、数据库在这点做了改造,解决了这个瓶颈。
【2】这样的量级数据从HBase 中提取到单节点内存中进行聚合运算,在资源消耗方面不可忽视。
【3】一个查询一旦提取的量级大,OpenTSDB 向HBase 发起RPC 请求(OpenTSDB 一次请求默认128行)次数也必然增加,十几秒、几十秒的响应时间,这就限制了它的应用场景。
架构中应用OpenTSDB 需要事先考虑
【1】OpenTSDB 只有4 张HBase 表,其中一张是存放数据。所有的数据都存放在一张表,这就意味应用在OpenTSDB 这个层级上是无法更小的粒度来区别对待不同业务(比如想把WebApp 性能指标放在一张表上,OS 性能指标放在一张表上),很难根据业务类别自身特点进行差异化对待后续的运维(比如不同类别设置不同的TTL,数据删除,迁移操作等等)。除非再部署多套OpenTSDB 来对待,这是另外一个话题。
【2】OpenTSDB 不支持二级索引,只有一个基于HBase的RowKey。结合业务场景的查询维度,设计好RowKey 是应用好OpenTSDB 的关键!提前评估好metric + tag 背后扫描的数据量。比如将high-cardinality tag 调整到metric 中,减少扫描的数据量。
【3】OpenTSDB 能实时聚合计算功能,但基于单点运算能力有限,建议这种聚合在入库阶段完成的。比如将1 分钟粒度聚合、5分钟粒度聚合提前通过KafkaStream,Spark 等运算,将聚会结果存入OpenTSDB 供查询。
【4】Tcollector采集数据上报给OpenTSDB,建议在中间加一层Kafka 。一来解偶两者之间的强依赖性,同时保存一段时间采集数据,在OpenTSDB、HBase 不可用(计划性运维、集群故障)时不至于丢失数据,对运维来说也增强了操作上的灵活性。二来可以对采集的原始数据进行二次加工再入OpenTSDB,如粒度聚合运算。
【5】关于salt 启用
时间序列数据的写入,写热点是一个不可规避的问题,当某一个metric 下数据点很多时,则该metric 很容易造成写入热点。从2.2 开始,OpenTSDB 采取了允许将metric 预分桶,预分桶后的变化就是在rowkey 前会拼上一个桶编号(bucket index)。预分桶后,可将某个热点metric 的写压力分散到多个桶中,避免了写热点的产生。而客户端查询OpenTSDB 一条数据,OpenTSDB 将这个请求拆成分桶数个查询到HBase,然后返回桶数个结果集到OpenTSDB 层做合并。对HBase 并发请求相应的也会桶数倍的扩大。
设计时需要结合查询场景,在查询性能和后期热点运维方面,做一个权衡考虑。比如说有个场景,在前端页面上要查询一个业务相关的性能指标(以业务为查询单元),查询的结果以多个图形方式展现出来,要求响应时间快。如果这是一个用户点击查询比较频繁的场景(并发量大),启动了salt,无形加大OpenTSDB和HBase的压力。如果完全关闭salt 的话,写热点问题就加大HBase 的运维难度,极端的话造成整个集群整体性能下降。如果写入吞吐量大的话,建议开启salt,但桶的个数不易太多,2-4个即可,默认是20 个桶。
比如查询一个业务AAA的数据库性能指标,如下图:
【6】OpenTSDB 提供三种途径写数据,分别是Telnet API, HTTP API, 批量导入。Telnet 读写是异步操作,但是返回的响应混乱的。HTTP API 是同步读写操作,成功与否能及时反馈,同时多个数据点能在单个端口发送,节省带宽。理论上Telnet 速度好于HTTP,但牺牲了数据是否成功写入的可靠性。在实际生产使用中,建议使用HTTP API,而Telnet API 用于运维。
【7】关于Cache
OpenTSDB 并没有内建的Cache 来存放数据,截止到2.3 还是只能依赖HBase 的Cache。
OpenTSDB 部署与运维方面
【1】OpenTSDB 是Java 实现的,JVM 方面的参数设置、GC、运维管理同样是OpenTSDB 需要考虑的。
【2】生产部署时,往往是多台机器部署,每台机器部署多个实例,读写分离。比如查询实例端口一律使用4242,写数据实例端口一律使用4243,然后读写请求通过nginx + Consul 均衡到各个实例端口上。
【3】设置tsd.storage.hbase.prefetch_meta = true(默认是false),否则极端情况下会打爆hbase:meta 表请求。
【4】2.2 版本开始,tsd 写数据到HBase有两种方式,一种是每来一条数据append 到hbase。另一种是先缓存大量数据到TSD 的内存里,然后进行compaction,一次性写入。官方推荐Append 方式。相关参数tsd.storage.enable_appends = true 。
【5】TSDB 在每小时整点的时候将上个小时的数据读出来(get),然后compact 成一个row,写入(put)到HBase,然后删除(delete)原始数据,目的是减少存储消耗,增大了HBase 压力,性能平稳上出现一定的波动。
【6】若metric, tag含有中文,编译时指定字符集为UTF8。
【7】其他一些注意的参数:
tsd.storage.hbase.scanner.maxNumRow
tsd.query.timeout
阿里巴巴在开源压测工具 JMeter 上的实践和优化
Apache JMeter 是 Apache 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。
性能测试通常集中在新系统上线或大型活动前(如电商大促,春节活动等),以验证系统能力,帮助排查定位性能瓶颈等问题。
一次压测活动可粗略分为几个步骤:
场景配置。配置压测场景模拟用户(业务)与系统的交互。
压测执行。按指定压力量级启动压测。
压测监控分析。压测中通常关注施压 RPS,成功率,业务响应时间(RT),网络带宽等关键指标。
报告总结。披露系统能力是否符合要求,同时沉淀记录系统性能演变和优化过程。
下面我们讨论如何使用 JMeter 完成上述步骤,及相关的最佳实践建议。
JMeter 使用 Java 开发,需要先安装 JDK 并配置好 PATH 环境变量,然后从官网下载 JMeter 二进制压缩包解压即可。
建议将 JMeter bin 目录也添加到 PATH 环境变量,这样在命令行下输入 jmeter 命令即可启动 JMeter 。
一、场景配置
简单 HTTP 请求配置最常见的压测场景即 HTTP 压测。
压测场景在 JMeter 脚本中叫做 Test Plan(压测计划),打开 JMeter 默认即为一个空 Test Plan 。JMeter 使用并发(线程)数控制压力大小,一个线程可看做一个执行请求的虚拟用户。在 Test Plan 上点击右键,添加一个 Thread Group(线程组)。
线程组默认为 1 个线程并只执行一次 1 次,这很方便测试执行脚本,保持此默认值即可。
JMeter 中发送请求的组件叫做 Sampler(采样器)。在 Thread Group 上单击右键,添加一个 HTTP Request 节点(采样器)。
HTTP 请求最关键的配置即 URL,JMeter 允许将 URL 协议类型(Protocol)、服务器名、请求路径(Path)等拆开单独配置。也可以直接将整个 URL(如 JMeter 主页 http://jmeter.apache.org/ )填写到 Path,其他字段保留为空即可。这样,一个最简单的 HTTP 压测脚本就配置好了。
为了方便测试、调试脚本,可在 Test Plan 下添加一个 View Results Tree 监听器(Listener)。这个监听器仅用于编辑脚本时测试、调试脚本,查看请求执行详情,不需要做任何配置。
测试执行脚本
第一次执行脚本前,需要先保存脚本,如保存为 test.jmx 。以后每次执行脚本前,JMeter 默认会自动保存脚本。
连续多次执行脚本时,JMeter 默认不会清理历史记录。为了避免历史执行结果干扰,可先点击 Clear All 按钮手动清空历史记录,再点击 Start 按钮执行脚本,这样看到的执行结果更清爽,方便排查问题。
按照默认线程组配置,脚本执行一次即结束。点击 View Results Tree ,可看到请求执行详细信息,包括请求头,请求体,响应头和完整的响应体等信息。
场景编排真实压测场景通常不会只有一个请求,而是多个请求按一定顺序和规则的编排组合,即场景编排。场景编排是 JMeter 等压测引擎最重要的功能之一,也是与 apache ab等简单压测工具的重要区别之一。
这里我们假设一个最简单的场景,先访问 JMeter 主页,停留 1 秒钟后跳转到下载页。
一个脚本访问一个网站的不同页面(Path)时,可添加一个 HTTP Request Defaults 节点,配置默认协议类型和服务器名。这样可避免重复配置,需要修改协议类型(如 https 与 http 切换)或压测域名时,只用修改 HTTP Request Defaults 即可。
HTTP Request Defaults 配置服务器名为 jmeter.apache.org(协议类型默认为 http),鼠标可拖动 HTTP Request Defaults 节点移动到 HTTP 请求节点之前。
每个请求节点可设置一个具有业务含义的名字,方便理解和管理。访问 JMeter 主页的 HTTP 请求可改名为 home ,同时 Path 修改为 / 。再添加一个 HTTP 请求节点,命名为 download page ,设置 Path 为 /download_jmeter.cgi 即可。
模拟在 home 页面停顿 1 秒钟。home 节点上右键,添加一个 Constant Timer 子节点,设置延迟时间为 1000 毫秒即可。
再次执行脚本,点击 View Results Tree 可看到两个 HTTP 请求节点的执行详情。
注意:
Timer 节点作为场景编排辅助节点,没有请求执行动作,也没有执行详情显示。
循环执行脚本时,最后一个节点 download page 执行结束后,会立即跳转到脚本开头,执行第一个节点 home 。
可在 download page 上也添加一个 Timer,模拟停留一秒之后再继续后续请求。
二、JMeter 的压测执行
编辑、调试脚本时,我们通常设置为 1 个线程并且只执行 1 次。执行压力测试时,通常需要以较高的压力持续执行一段时间。
脚本固定配置压力如计划以 50 并发执行 2 分钟,可修改脚本 Thread Group 配置如下。
配置说明:
并发数(Number of Threads (users))设置为 50 。
循环次数(Loop Count)勾选永远执行(Forever)。
勾选 Scheduler,设置执行时长(Duration (seconds))为 120 秒。
通常我们在 JMeter 图形界面(GUI)编辑脚本,但执行压力测试时 GUI 占用额外资源可能影响施压性能,而且施压机可能没有图形界面环境(如 ssh 远程登录施压机)。因此脚本编辑完成后,通常以命令行模式执行 JMeter 压力测试。
进入 JMeter 脚本目录,执行 JMeter 压力测试的命令为:
jmeter -n -t <脚本>
如执行上述 test.jmx 脚本,命令如下:
jmeter -n -t test.jmx
输出结果如下:
Creating summariser <summary>
Created the tree successfully using test.jmx
Starting the test @ Tue Jun 25 14:38:32 CST 2019 (1561444712414)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary + 553 in 00:00:27 = 20.3/s Avg: 1378 Min: 252 Max: 8587 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary + 882 in 00:00:30 = 29.4/s Avg: 685 Min: 222 Max: 4272 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 1435 in 00:00:57 = 25.1/s Avg: 952 Min: 222 Max: 8587 Err: 0 (0.00%)
summary + 829 in 00:00:30 = 27.5/s Avg: 815 Min: 222 Max: 21310 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 2264 in 00:01:27 = 25.9/s Avg: 902 Min: 222 Max: 21310 Err: 0 (0.00%)
summary + 881 in 00:00:30 = 29.5/s Avg: 700 Min: 221 Max: 3896 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 3145 in 00:01:57 = 26.8/s Avg: 845 Min: 221 Max: 21310 Err: 0 (0.00%)
summary + 127 in 00:00:05 = 24.2/s Avg: 797 Min: 224 Max: 3819 Err: 0 (0.00%) Active: 0 Started: 50 Finished: 50
summary = 3272 in 00:02:02 = 26.7/s Avg: 843 Min: 221 Max: 21310 Err: 0 (0.00%)
Tidying up ... @ Tue Jun 25 14:40:35 CST 2019 (1561444835251)
... end of run
压测过程中默认每 30 秒输出一次统计数据,2 分钟后(实际为 00:02:02,比预设的 2 分钟多出少许误差)压测结束。看最后一行统计数据,平均 RPS(每秒请求数)为 26.7,平均 RT (响应时间)为 843 毫秒。
尝试核对一下统计数据。脚本包含两个请求,每个请求附加 1 秒钟等待时间,发送一个请求平均耗时为 RT 843 毫秒 + 等待 1000 毫秒。单线程理论 RPS 为 1000.0 / (843 + 1000),总共 50 个线程,全场景理论 RPS 为 1000.0 / (843 + 1000) * 50 = 27.13,与统计值 26.7 有一定误差。这是因为除了请求 RT 和等待时间,脚本执行请求之间还可能存在少量时间消耗。
命令行动态设置压力实际工作中,常常需要以不同的压力大小反复执行压力测试,在脚本中写死压力大小(并发数)和执行时间显然很不方便。
如何动态指定压力大小呢,这里有一个技巧。JMeter 脚本支持使用 JMeter 属性进行配置,JMeter 命令行支持使用 -J 参数动态指定 JMeter 属性。把这两者结合起来,即可实现在命令行通过 -J参数动态设置压力大小。
修改 JMeter 脚本使用 JMeter 属性配置压力大小,配置如下:
配置说明:
并发数配置为 ${__P(load.concurrency,1)},循环次数取消勾选 Forever,配置为 ${__P(load.count,1)}。未设置对应的 JMeter 属性时,默认为 1 ,满足只执行 1 次以测试、调试脚本的需求。
执行时长配置为 ${__P(load.duration,60)},默认 1 分钟(60 秒)。
测试命令行直接执行脚本:
jmeter -n -t test.jmx
可看到统计输出如下:
summary = 2 in 00:00:03 = 0.6/s Avg: 485 Min: 408 Max: 563 Err: 0 (0.00%)
默认一个并发并且只执行一次,发出 2 个请求(脚本循环一次发出两个请求),约 3 秒后脚本停止。注意:默认执行时间是 1 分钟,同时配置了循环次数和执行时间时,有一个条件先满足脚本即停止。
为了按指定时长执行,需要将执行次数设置为 Forever 。在 JMeter 内部实现中,执行次数为 -1 即表示 Forever 。指定以 50 并发执行 2 分钟,jmeter 命令行如下:
jmeter -n -t test.jmx -Jload.concurrency=50 -Jload.duration=120 -Jload.count=-1
执行结果与前述脚本固定配置 50 并发的结果类似。
三、云上的 JMeter 实践
阿里巴巴有着非常丰富的业务形态,每一种业务形态背后都由一系列分布式的技术体系提供服务,随着业务的快速发展,特别是在双11等大促营销等活动场景下,准确评估整个业务站点的服务能力成为一大技术难题。
在这个过程中,我们打造了自己的全链路压测系统,以应对更复杂、更多样的压测需求,并将此技术输出到 性能测试PTS 上,同时支持原生 JMeter 压测。
打开 PTS 控制台 主页,左侧导航栏选择 创建压测 > JMeter 压测 ,新建 JMeter 压测场景。填写场景名,如 jmeter-test 。场景配置 页面点击 上传文件 按钮,上传本地测试通过的 test.jmx 脚本。
施压配置 页面,并发数设置为 50,压测时长设置为 2 分钟。
点击 保存去压测,弹出提示框点击 确认,PTS 即开始在云端引擎执行 JMeter 脚本发起压力。
压测中页面如下:
压测中实时展示场景(及每个请求页面)实时 RPS 和 RT 等信息。可看到场景并发数为 50,RPS 为 26,RT 为 812 毫秒,与本地压测的结果差不多。
注意:因为机器配置和网络环境的差异(PTS 施压机默认为 4核 8G,BGP多线路公网),PTS 上压测结果可能与本地压测结果存在一定差异。
针对 JMeter 施压配置,再补充几点说明:
PTS 上的施压配置会覆盖原脚本中的配置。原脚本无论是写死固定配置还是使用 JMeter 属性配置都没关系。
循环次数表示每个线程循环执行脚本的次数,可能与用户的直觉理解不一样,如 总请求数 = 脚本执行一次的请求数 循环次数 并发数 。
循环次数与预热时间同时配置时可能导致意外行为(如不能达到最大并发),因此 PTS 上不允许同时配置预热时间和循环次数(即只有预热时间为 0 时才允许设置循环次数)。
四、压测监控分析
性能测试不仅仅是简单的发起压力,对压力负载(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 采取了一个折中的办法,施压引擎每 5 秒对每个业务 API(压测Sampler)分别采样记录一条成功和失败(如果有)的请求详细信息。在压测中或压测报告页面,点击 查看采样日志 按钮即可查询记录的请求采样信息,并支持按业务 API(压测Sampler),响应状态(是否成功),请求时间等进行搜索过滤。
点击 查看详情 即可看到单个请求的详细信息。目前对详细信息提供了通用和 HTTP 两种展示模板,HTTP 展示模板可针对 HTTP 请求进行更友好的排版展示,展示内容包括请求 URL,请求方法,返回码,完整的请求头、请求体,响应头、响应体等。
因为页面上只展示文本内容,请求体或响应体包含图片等无法识别为文本的内容时,可能显示为乱码。另外当请求体或响应体很大时,对应的内容可能被截断。
JMeter 日志本地执行 JMeter 脚本时,默认将日志记录到 jmeter.log 文件。在 PTS 上执行 JMeter 脚本时,可通过 JMeter日志 页面实时查看 JMeter 日志,并支持根据日志级别、时间或线程名进行查询过滤。
JMeter 日志主要用于脚本执行报错时排查错误原因。一些插件可能通过 JMeter 日志输出一些重要信息,用户在 groovy 脚本等代码中也可以直接打印日志。
日志打印过于频繁时,不仅可读性极差(大量重复日志淹没重要信息),而且影响 JMeter 性能,对 PTS 采集存储 JMeter 日志造成额外开销。因此 PTS 在采集 JMeter 日志时默认进行了限流,每秒钟打印日志条数超过 10 条时部分日志可能会丢失。良好设计的 JMeter 脚本应避免大量打印重复日志。
同样,良好设计的 JMeter 脚本应避免通过标准输出(System.out)或标准错误(System.err)打印输出信息,需要输出查看的重要信息应使用 JMeter 日志输出(如 groovy 脚本中使用 log.info("") )。PTS 上不支持查看 JMeter 脚本执行产生的标准输出和标准错误内容。
五、报告总结
压测结束后,PTS 将汇总监控数据形成压测报告。用户根据压测报告分析评估系统性能是否符合要求,如 RPS,成功率和 RT(响应时间)是否符合期望。并可辅助用户排查分析业务系统性能瓶颈。
PTS 压测报告页面可查询历史压测报告列表。
点击 查看报告 打开查看报告详情。压测报告在 PTS 上默认保存 30 天,可点击 报告导出 按钮,导出保存 PDF 版压测报告到本地。
压测报告概要信息包括压测执行时间,RPS,RT,成功率等概要数据。场景详情包含全场景维度和业务 API 维度的监控统计信息。注意:受 JMeter 引擎本身统计功能的限制,仅全场景维度包含并发数统计。
此外,全场景维度和业务 API 维度均包含 RPS,成功率,网络带宽等统计。如 home 请求相关监控趋势图如下。
相比手动命令行执行 JMeter 脚本,PTS 更加 简单易用 ,提供 简单直观的监控 ,并提供 海量施压能力 。
免费 开通 PTS 服务,购买 5000 VU以上资源包,即可使用 JMeter 压测。PTS 计费单位是 VUM ,即并发(VU) 分钟(最低消费 100 VUM)。上述场景消费为 50 并发 2 分钟 = 100 VUM 。如购买 5000 VU资源包,包含 10 万 VUM,售价 ¥278 ,上述计费可换算为 (100 / 10万) * ¥278 = ¥0.278 。
六、使用 CSV 参数文件
上述 JMeter 脚本仅简单请求固定 URL ,真实业务 API 通常带有请求参数。
JMeter 中可使用 CSV Data Set Config 读取 CSV 数据文件,简单实现请求参数化。CSV 文件默认首行为变量名(列名),其余行为 CSV 数据。准备一个 user.csv 文件,包含 id , name 两列,内容如下:
id,name
1,ali
2,pts
3,jmeter
注意:手工编辑 CSV 文件容易出错,推荐使用 Excel、Numbers 等软件导出,或编程使用 apache commons-csv 生成。编辑 JMeter 脚本,右键单击 Thread Group 添加一个 CSV Data Set Config节点。
配置如下:
其中有两个地方需要注意(其他配置保持默认即可):
1.Filename 配置为文件名 user.csv 即可,不要包含文件路径。不同施压机上 CSV 文件路径可能不一样,只使用文件名(并在当前路径下执行脚本)以便兼容不同的施压机环境。
2.Sharing mode 设置为 Current thread group,指定 CSV 文件只被当前线程组使用。
假设 home 请求需要设置参数,配置请求参数直接使用 ${id} ,${name} 引用对应的变量即可。
测试执行脚本,查看 View Results Tree ,可看到执行请求时即会带上请求参数。
在阿里云 PTS 上执行脚本,只需编辑 JMeter 场景,上传修改后的 test.jmx 脚本文件和 user.csv数据文件,点击 保存去压测 即可。
查看请求采样,可看到请求 URL 已添加对应的参数。
**使用 JMeter 插件和附加 jar 包**JMeter 社区提供了丰富的插件,用户还可以自由添加使用 Java 库 Jar 包。在 PTS 上执行脚本时,只需要将额外添加的插件和 jar 包一起上传到 PTS 上,PTS 执行 JMeter 脚本时即可自动加载这些 Jar 包。详情可参考 如何进行 WebSocket 协议的压测 。
如果遇到问题,请先确认本地已测试通过,并确认额外使用的 Jar 包已全部上传到 PTS 。
七、海量施压能力
之前的脚本中,我们在请求后添加了 1 秒钟的等待时间以演示业务场景编排。如果压测场景前后请求没有关联,从服务器的角度看只是不停的收到各种请求,与客户端是否等待无关,因此 JMeter 脚本可去掉这些等待时间,以最大压力向服务器发起极限压测。
JMeter 使用并发(线程)数控制压力大小,通常(并发较小时)线程数越多压力越大。但单台施压机的能力毕竟有限,单机线程数增加到某个阈值时请求压力即达到极限。显然,为了继续增加压力,必须使用多台施压机进行分布式压测。
分布式压测需要注意以下几点:
每台施压机需要安装、启动 JMeter,拷贝分发脚本、CSV 数据、附加 jar 包等文件。
协调分配施压并发数。通常施压机配置相同,均分施压并发数即可。
统一控制启动、停止压测,统一收集聚合监控数据。
希望 CSV 数据唯一或打散数据时,多台施压机需要拆分 CSV 数据文件 。
使用阿里云 PTS 执行 JMeter 脚本时,用户只需要设置期望的最大并发即可 ,PTS 自动透明处理了分布式压测的问题,用户不用关心是单机压测还是分布式压测。PTS 默认每台 JMeter 施压机最大分配 500 并发,场景并发数超过 500 时即自动分配多台施压机。如果希望多台施压机拆分 CSV 数据文件,只要在上传 CSV 文件后勾选 切分文件 即可。详情参考 切分 CSV 数据文件 。
单机线程数极限与 JMeter 脚本的复杂程度和资源占用(如 CPU,内存,网络带宽占用等)情况有关。占用内存过高的 JMeter 脚本在并发过高时甚至可能会因内存溢出而导致 JMeter 引擎异常退出。对有特殊需求的高级用户,PTS 允许用户手动指定施压机数量,以更精确的控制总并发数和单机并发数。只需在 施压配置 页面勾选 指定IP数,设置相应的施压机数量即可。每台施压机有一个IP地址,指定IP数即指定施压机数量。
注意:指定IP数后,压测执行时独占了指定数量的施压机,每台施压机将按 500 并发计费(无论实际并发多少)。此功能专为有特殊需求的高级用户提供,普通用户通常不需要使用。
云时代 JMeter 最佳实践
Apache JMeter 是 Apache 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。
性能测试通常集中在新系统上线或大型活动前(如电商大促,春节活动等),以验证系统能力,帮助排查定位性能瓶颈等问题。
一次压测活动可粗略分为几个步骤:
场景配置。配置压测场景模拟用户(业务)与系统的交互。
压测执行。按指定压力量级启动压测。
压测监控分析。压测中通常关注施压 RPS,成功率,业务响应时间(RT),网络带宽等关键指标。
报告总结。披露系统能力是否符合要求,同时沉淀记录系统性能演变和优化过程。
下面我们讨论如何使用 JMeter 完成上述步骤,及相关的最佳实践建议。
JMeter 使用 Java 开发,需要先安装 JDK 并配置好 PATH 环境变量,然后从官网下载 JMeter 二进制压缩包解压即可。建议将 JMeter bin 目录也添加到 PATH 环境变量,这样在命令行下输入 jmeter 命令即可启动 JMeter 。
场景配置
简单 HTTP 请求配置
最常见的压测场景即 HTTP 压测。压测场景在 JMeter 脚本中叫做 Test Plan(压测计划),打开 JMeter 默认即为一个空 Test Plan 。JMeter 使用并发(线程)数控制压力大小,一个线程可看做一个执行请求的虚拟用户。在 Test Plan 上点击右键,添加一个 Thread Group(线程组)。
线程组默认为 1 个线程并只执行一次 1 次,这很方便测试执行脚本,保持此默认值即可。
JMeter 中发送请求的组件叫做 Sampler(采样器)。在 Thread Group 上单击右键,添加一个 HTTP Request 节点(采样器)。
HTTP 请求最关键的配置即 URL,JMeter 允许将 URL 协议类型(Protocol)、服务器名、请求路径(Path)等拆开单独配置。也可以直接将整个 URL(如 JMeter 主页 http://jmeter.apache.org/ )填写到 Path,其他字段保留为空即可。这样,一个最简单的 HTTP 压测脚本就配置好了。
为了方便测试、调试脚本,可在 Test Plan 下添加一个 View Results Tree 监听器(Listener)。这个监听器仅用于编辑脚本时测试、调试脚本,查看请求执行详情,不需要做任何配置。
测试执行脚本
第一次执行脚本前,需要先保存脚本,如保存为 test.jmx 。以后每次执行脚本前,JMeter 默认会自动保存脚本。
连续多次执行脚本时,JMeter 默认不会清理历史记录。为了避免历史执行结果干扰,可先点击 Clear All 按钮手动清空历史记录,再点击 Start 按钮执行脚本,这样看到的执行结果更清爽,方便排查问题。
按照默认线程组配置,脚本执行一次即结束。点击 View Results Tree ,可看到请求执行详细信息,包括请求头,请求体,响应头和完整的响应体等信息。
场景编排
真实压测场景通常不会只有一个请求,而是多个请求按一定顺序和规则的编排组合,即场景编排。场景编排是 JMeter 等压测引擎最重要的功能之一,也是与 apache ab 等简单压测工具的重要区别之一。
这里我们假设一个最简单的场景,先访问 JMeter 主页,停留 1 秒钟后跳转到下载页。
一个脚本访问一个网站的不同页面(Path)时,可添加一个 HTTP Request Defaults 节点,配置默认协议类型和服务器名。这样可避免重复配置,需要修改协议类型(如 https 与 http 切换)或压测域名时,只用修改 HTTP Request Defaults 即可。
HTTP Request Defaults 配置服务器名为 jmeter.apache.org(协议类型默认为 http),鼠标可拖动 HTTP Request Defaults 节点移动到 HTTP 请求节点之前。
每个请求节点可设置一个具有业务含义的名字,方便理解和管理。访问 JMeter 主页的 HTTP 请求可改名为 home ,同时 Path 修改为 / 。再添加一个 HTTP 请求节点,命名为 download page ,设置 Path 为 /download_jmeter.cgi 即可。
模拟在 home 页面停顿 1 秒钟。home 节点上右键,添加一个 Constant Timer 子节点,设置延迟时间为 1000 毫秒即可。
再次执行脚本,点击 View Results Tree 可看到两个 HTTP 请求节点的执行详情。
注意:
Timer 节点作为场景编排辅助节点,没有请求执行动作,也没有执行详情显示。
循环执行脚本时,最后一个节点 download page 执行结束后,会立即跳转到脚本开头,执行第一个节点 home 。可在 download page 上也添加一个 Timer,模拟停留一秒之后再继续后续请求。
压测执行
编辑、调试脚本时,我们通常设置为 1 个线程并且只执行 1 次。执行压力测试时,通常需要以较高的压力持续执行一段时间。
脚本固定配置压力
如计划以 50 并发执行 2 分钟,可修改脚本 Thread Group 配置如下。
配置说明:
并发数(Number of Threads (users))设置为 50 。
循环次数(Loop Count)勾选永远执行(Forever)。
勾选 Scheduler,设置执行时长(Duration (seconds))为 120 秒。
通常我们在 JMeter 图形界面(GUI)编辑脚本,但执行压力测试时 GUI 占用额外资源可能影响施压性能,而且施压机可能没有图形界面环境(如 ssh 远程登录施压机)。因此脚本编辑完成后,通常以命令行模式执行 JMeter 压力测试。
进入 JMeter 脚本目录,执行 JMeter 压力测试的命令为:
jmeter -n -t <脚本>
如执行上述 test.jmx 脚本,命令如下:
jmeter -n -t test.jmx
输出结果如下:
Creating summariser <summary>
Created the tree successfully using test.jmx
Starting the test @ Tue Jun 25 14:38:32 CST 2019 (1561444712414)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary + 553 in 00:00:27 = 20.3/s Avg: 1378 Min: 252 Max: 8587 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary + 882 in 00:00:30 = 29.4/s Avg: 685 Min: 222 Max: 4272 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 1435 in 00:00:57 = 25.1/s Avg: 952 Min: 222 Max: 8587 Err: 0 (0.00%)
summary + 829 in 00:00:30 = 27.5/s Avg: 815 Min: 222 Max: 21310 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 2264 in 00:01:27 = 25.9/s Avg: 902 Min: 222 Max: 21310 Err: 0 (0.00%)
summary + 881 in 00:00:30 = 29.5/s Avg: 700 Min: 221 Max: 3896 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 3145 in 00:01:57 = 26.8/s Avg: 845 Min: 221 Max: 21310 Err: 0 (0.00%)
summary + 127 in 00:00:05 = 24.2/s Avg: 797 Min: 224 Max: 3819 Err: 0 (0.00%) Active: 0 Started: 50 Finished: 50
summary = 3272 in 00:02:02 = 26.7/s Avg: 843 Min: 221 Max: 21310 Err: 0 (0.00%)
Tidying up ... @ Tue Jun 25 14:40:35 CST 2019 (1561444835251)
... end of run
压测过程中默认每 30 秒输出一次统计数据,2 分钟后(实际为 00:02:02,比预设的 2 分钟多出少许误差)压测结束。看最后一行统计数据,平均 RPS(每秒请求数)为 26.7,平均 RT (响应时间)为 843 毫秒。
尝试核对一下统计数据。脚本包含两个请求,每个请求附加 1 秒钟等待时间,发送一个请求平均耗时为 RT 843 毫秒 + 等待 1000 毫秒。单线程理论 RPS 为 1000.0 / (843 + 1000),总共 50 个线程,全场景理论 RPS 为 1000.0 / (843 + 1000) * 50 = 27.13,与统计值 26.7 有一定误差。这是因为除了请求 RT 和等待时间,脚本执行请求之间还可能存在少量时间消耗。
命令行动态设置压力
实际工作中,常常需要以不同的压力大小反复执行压力测试,在脚本中写死压力大小(并发数)和执行时间显然很不方便。
如何动态指定压力大小呢,这里有一个技巧。JMeter 脚本支持使用 JMeter 属性进行配置,JMeter 命令行支持使用 -J 参数动态指定 JMeter 属性。把这两者结合起来,即可实现在命令行通过 -J 参数动态设置压力大小。
修改 JMeter 脚本使用 JMeter 属性配置压力大小,配置如下:
配置说明:
并发数配置为 ${__P(load.concurrency,1)},循环次数取消勾选 Forever,配置为 ${__P(load.count,1)}。未设置对应的 JMeter 属性时,默认为 1 ,满足只执行 1 次以测试、调试脚本的需求。
执行时长配置为 ${__P(load.duration,60)},默认 1 分钟(60 秒)。
测试命令行直接执行脚本:
jmeter -n -t test.jmx
可看到统计输出如下:
summary = 2 in 00:00:03 = 0.6/s Avg: 485 Min: 408 Max: 563 Err: 0 (0.00%)
默认一个并发并且只执行一次,发出 2 个请求(脚本循环一次发出两个请求),约 3 秒后脚本停止。注意:默认执行时间是 1 分钟,同时配置了循环次数和执行时间时,有一个条件先满足脚本即停止。
为了按指定时长执行,需要将执行次数设置为 Forever 。在 JMeter 内部实现中,执行次数为 -1 即表示 Forever 。指定以 50 并发执行 2 分钟,jmeter 命令行如下:
jmeter -n -t test.jmx -Jload.concurrency=50 -Jload.duration=120 -Jload.count=-1
执行结果与前述脚本固定配置 50 并发的结果类似。
阿里云 PTS 执行 JMeter 脚本
阿里云 PTS 是一款云化性能测试工具,同时也支持原生 JMeter 压测。
打开 PTS 控制台 主页,左侧导航栏选择 创建压测 > JMeter 压测 ,新建 JMeter 压测场景。填写场景名,如 jmeter-test 。场景配置 页面点击 上传文件 按钮,上传本地测试通过的 test.jmx 脚本。
施压配置 页面,并发数设置为 50,压测时长设置为 2 分钟。
点击 保存去压测,弹出提示框点击 确认,PTS 即开始在云端引擎执行 JMeter 脚本发起压力。
压测中页面如下:
压测中实时展示场景(及每个请求页面)实时 RPS 和 RT 等信息。可看到场景并发数为 50,RPS 为 26,RT 为 812 毫秒,与本地压测的结果差不多。注意:因为机器配置和网络环境的差异(PTS 施压机默认为 4核 8G,BGP多线路公网),PTS 上压测结果可能与本地压测结果存在一定差异。
针对 JMeter 施压配置,再补充几点说明:
PTS 上的施压配置会覆盖原脚本中的配置。原脚本无论是写死固定配置还是使用 JMeter 属性配置都没关系。
循环次数表示每个线程循环执行脚本的次数,可能与用户的直觉理解不一样,如 总请求数 = 脚本执行一次的请求数 * 循环次数 * 并发数 。
循环次数与预热时间同时配置时可能导致意外行为(如不能达到最大并发),因此 PTS 上不允许同时配置预热时间和循环次数(即只有预热时间为 0 时才允许设置循环次数)。
压测监控分析
性能测试不仅仅是简单的发起压力,对压力负载(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 采取了一个折中的办法,施压引擎每 5 秒对每个业务 API(压测Sampler)分别采样记录一条成功和失败(如果有)的请求详细信息。在压测中或压测报告页面,点击 查看采样日志 按钮即可查询记录的请求采样信息,并支持按业务 API(压测Sampler),响应状态(是否成功),请求时间等进行搜索过滤。
点击 查看详情 即可看到单个请求的详细信息。目前对详细信息提供了通用和 HTTP 两种展示模板,HTTP 展示模板可针对 HTTP 请求进行更友好的排版展示,展示内容包括请求 URL,请求方法,返回码,完整的请求头、请求体,响应头、响应体等。
因为页面上只展示文本内容,请求体或响应体包含图片等无法识别为文本的内容时,可能显示为乱码。另外当请求体或响应体很大时,对应的内容可能被截断。
JMeter 日志
本地执行 JMeter 脚本时,默认将日志记录到 jmeter.log 文件。在 PTS 上执行 JMeter 脚本时,可通过 JMeter日志 页面实时查看 JMeter 日志,并支持根据日志级别、时间或线程名进行查询过滤。
JMeter 日志主要用于脚本执行报错时排查错误原因。一些插件可能通过 JMeter 日志输出一些重要信息,用户在 groovy 脚本等代码中也可以直接打印日志。
日志打印过于频繁时,不仅可读性极差(大量重复日志淹没重要信息),而且影响 JMeter 性能,对 PTS 采集存储 JMeter 日志造成额外开销。因此 PTS 在采集 JMeter 日志时默认进行了限流,每秒钟打印日志条数超过 10 条时部分日志可能会丢失。良好设计的 JMeter 脚本应避免大量打印重复日志。
同样,良好设计的 JMeter 脚本应避免通过标准输出(System.out)或标准错误(System.err)打印输出信息,需要输出查看的重要信息应使用 JMeter 日志输出(如 groovy 脚本中使用 log.info("") )。PTS 上不支持查看 JMeter 脚本执行产生的标准输出和标准错误内容。
报告总结
压测结束后,PTS 将汇总监控数据形成压测报告。用户根据压测报告分析评估系统性能是否符合要求,如 RPS,成功率和 RT(响应时间)是否符合期望。并可辅助用户排查分析业务系统性能瓶颈。
PTS 压测报告页面可查询历史压测报告列表。
点击 查看报告 打开查看报告详情。压测报告在 PTS 上默认保存 30 天,可点击 报告导出 按钮,导出保存 PDF 版压测报告到本地。压测报告概要信息包括压测执行时间,RPS,RT,成功率等概要数据。场景详情包含全场景维度和业务 API 维度的监控统计信息。注意:受 JMeter 引擎本身统计功能的限制,仅全场景维度包含并发数统计。
此外,全场景维度和业务 API 维度均包含 RPS,成功率,网络带宽等统计。如 home 请求相关监控趋势图如下。
相比手动命令行执行 JMeter 脚本,PTS 更加 简单易用 ,提供 简单直观的监控 ,并提供 海量施压能力 。
免费 开通 PTS 服务,购买 5000 VU 以上资源包,即可使用 JMeter 压测。PTS 计费单位是 VUM ,即并发(VU)* 分钟(最低消费 100 VUM)。上述场景消费为 50 并发 * 2 分钟 = 100 VUM 。如购买 5000 VU 资源包,包含 10 万 VUM,售价 ¥278 ,上述计费可换算为 (100 / 10万) * ¥278 = ¥0.278 。
使用 CSV 参数文件
上述 JMeter 脚本仅简单请求固定 URL ,真实业务 API 通常带有请求参数。
JMeter 中可使用 CSV Data Set Config 读取 CSV 数据文件,简单实现请求参数化。CSV 文件默认首行为变量名(列名),其余行为 CSV 数据。准备一个 user.csv 文件,包含 id , name 两列,内容如下:
id,name
1,ali
2,pts
3,jmeter
注意:手工编辑 CSV 文件容易出错,推荐使用 Excel、Numbers 等软件导出,或编程使用 apache commons-csv 生成。
编辑 JMeter 脚本,右键单击 Thread Group 添加一个 CSV Data Set Config 节点。
配置如下:
其中有两个地方需要注意(其他配置保持默认即可):
Filename 配置为文件名 user.csv 即可,不要包含文件路径。不同施压机上 CSV 文件路径可能不一样,只使用文件名(并在当前路径下执行脚本)以便兼容不同的施压机环境。
Sharing mode 设置为 Current thread group,指定 CSV 文件只被当前线程组使用。
假设 home 请求需要设置参数,配置请求参数直接使用 ${id} ,${name} 引用对应的变量即可。
测试执行脚本,查看 View Results Tree ,可看到执行请求时即会带上请求参数。
在阿里云 PTS 上执行脚本,只需编辑 JMeter 场景,上传修改后的 test.jmx 脚本文件和 user.csv 数据文件,点击 保存去压测 即可。
查看请求采样,可看到请求 URL 已添加对应的参数。
使用 JMeter 插件和附加 jar 包
JMeter 社区提供了丰富的插件,用户还可以自由添加使用 java 库 jar 包。在 PTS 上执行脚本时,只需要将额外添加的插件和 jar 包一起上传到 PTS 上,PTS 执行 JMeter 脚本时即可自动加载这些 jar 包。详情可参考 如何进行 WebSocket 协议的压测 。
如果遇到问题,请先确认本地已测试通过,并确认额外使用的 jar 包已全部上传到 PTS 。
海量施压能力
之前的脚本中,我们在请求后添加了 1 秒钟的等待时间以演示业务场景编排。如果压测场景前后请求没有关联,从服务器的角度看只是不停的收到各种请求,与客户端是否等待无关,因此 JMeter 脚本可去掉这些等待时间,以最大压力向服务器发起极限压测。
JMeter 使用并发(线程)数控制压力大小,通常(并发较小时)线程数越多压力越大。但单台施压机的能力毕竟有限,单机线程数增加到某个阈值时请求压力即达到极限。显然,为了继续增加压力,必须使用多台施压机进行分布式压测。
分布式压测需要注意以下几点:
每台施压机需要安装、启动 JMeter,拷贝分发脚本、CSV 数据、附加 jar 包等文件。
协调分配施压并发数。通常施压机配置相同,均分施压并发数即可。
统一控制启动、停止压测,统一收集聚合监控数据。
希望 CSV 数据唯一或打散数据时,多台施压机需要拆分 CSV 数据文件 。
使用阿里云 PTS 执行 JMeter 脚本时,用户只需要设置期望的最大并发即可 ,PTS 自动透明处理了分布式压测的问题,用户不用关心是单机压测还是分布式压测。PTS 默认每台 JMeter 施压机最大分配 500 并发,场景并发数超过 500 时即自动分配多台施压机。如果希望多台施压机拆分 CSV 数据文件,只要在上传 CSV 文件后勾选 切分文件 即可。详情参考 切分 CSV 数据文件 。
单机线程数极限与 JMeter 脚本的复杂程度和资源占用(如 CPU,内存,网络带宽占用等)情况有关。占用内存过高的 JMeter 脚本在并发过高时甚至可能会因内存溢出而导致 JMeter 引擎异常退出。对有特殊需求的高级用户,PTS 允许用户手动指定施压机数量,以更精确的控制总并发数和单机并发数。只需在 施压配置 页面勾选 指定IP数,设置相应的施压机数量即可。每台施压机有一个IP地址,指定IP数即指定施压机数量。
注意:指定IP数后,压测执行时独占了指定数量的施压机,每台施压机将按 500 并发计费(无论实际并发多少)。此功能专为有特殊需求的高级用户提供,普通用户通常不需要使用。
PTS 自研引擎
JMeter 压测主要面向熟悉 JMeter 的用户,或已有存量 JMeter 脚本的场景。
PTS 同时自研了一套压测引擎,具备以下优点:
在线编辑场景,更加简单易用。
压测中支持手动实时调速。
支持并发模式和 RPS 模式(技术专利)两种施压模式。
特有云端录制器。
支持更高量级的海量压测能力(百万级并发)。
PTS 自研引擎目前主要支持 HTTP 压测,推荐 HTTP 压测需求的用户使用。使用入门请参考 如何在一分钟内发起压测 。
更多信息请参考 性能测试 PTS 文档 。
阿里巴巴在开源压测工具 JMeter 上的实践和优化
本文是 《如何做好性能压测》系列专题分享的第三期,该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解,以帮助大家构建完整的性能压测的理论体系,并提供有例可依的实战。
该系列专题分享由阿里巴巴 PTS 团队出品,欢迎加入性能压测技术交流钉钉群(推荐):23380915,参与《如何做好性能压测》线上直播分享,若没有钉钉,请添加微信zjjxg2018,进入微信群。
第一期:《压测环境的设计和搭建》,点击这里。第二期:《性能压测工具选型对比》,点击这里。
第三期将分享阿里巴巴在 JMeter 上的实践,并结合自研产品一一破解在 JMeter 上遇到的不足。
Apache JMeter 是 Apache 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。
性能测试通常集中在新系统上线或大型活动前(如电商大促,春节活动等),以验证系统能力,帮助排查定位性能瓶颈等问题。
一次压测活动可粗略分为几个步骤:
场景配置。配置压测场景模拟用户(业务)与系统的交互。
压测执行。按指定压力量级启动压测。
压测监控分析。压测中通常关注施压 RPS,成功率,业务响应时间(RT),网络带宽等关键指标。
报告总结。披露系统能力是否符合要求,同时沉淀记录系统性能演变和优化过程。
下面我们讨论如何使用 JMeter 完成上述步骤,及相关的最佳实践建议。
JMeter 使用 Java 开发,需要先安装 JDK 并配置好 PATH 环境变量,然后从官网下载 JMeter 二进制压缩包解压即可。建议将 JMeter bin 目录也添加到 PATH 环境变量,这样在命令行下输入 jmeter 命令即可启动 JMeter 。
场景配置
简单 HTTP 请求配置
最常见的压测场景即 HTTP 压测。压测场景在 JMeter 脚本中叫做 Test Plan(压测计划),打开 JMeter 默认即为一个空 Test Plan 。JMeter 使用并发(线程)数控制压力大小,一个线程可看做一个执行请求的虚拟用户。在 Test Plan 上点击右键,添加一个 Thread Group(线程组)。
线程组默认为 1 个线程并只执行一次 1 次,这很方便测试执行脚本,保持此默认值即可。
JMeter 中发送请求的组件叫做 Sampler(采样器)。在 Thread Group 上单击右键,添加一个 HTTP Request 节点(采样器)。
HTTP 请求最关键的配置即 URL,JMeter 允许将 URL 协议类型(Protocol)、服务器名、请求路径(Path)等拆开单独配置。也可以直接将整个 URL(如 JMeter 主页 http://jmeter.apache.org/ )填写到 Path,其他字段保留为空即可。这样,一个最简单的 HTTP 压测脚本就配置好了。
为了方便测试、调试脚本,可在 Test Plan 下添加一个 View Results Tree 监听器(Listener)。这个监听器仅用于编辑脚本时测试、调试脚本,查看请求执行详情,不需要做任何配置。
测试执行脚本
第一次执行脚本前,需要先保存脚本,如保存为 test.jmx 。以后每次执行脚本前,JMeter 默认会自动保存脚本。
连续多次执行脚本时,JMeter 默认不会清理历史记录。为了避免历史执行结果干扰,可先点击 Clear All 按钮手动清空历史记录,再点击 Start 按钮执行脚本,这样看到的执行结果更清爽,方便排查问题。
按照默认线程组配置,脚本执行一次即结束。点击 View Results Tree ,可看到请求执行详细信息,包括请求头,请求体,响应头和完整的响应体等信息。
场景编排
真实压测场景通常不会只有一个请求,而是多个请求按一定顺序和规则的编排组合,即场景编排。场景编排是 JMeter 等压测引擎最重要的功能之一,也是与 apache ab等简单压测工具的重要区别之一。
这里我们假设一个最简单的场景,先访问 JMeter 主页,停留 1 秒钟后跳转到下载页。
一个脚本访问一个网站的不同页面(Path)时,可添加一个 HTTP Request Defaults 节点,配置默认协议类型和服务器名。这样可避免重复配置,需要修改协议类型(如 https 与 http 切换)或压测域名时,只用修改 HTTP Request Defaults 即可。
HTTP Request Defaults 配置服务器名为 jmeter.apache.org(协议类型默认为 http),鼠标可拖动 HTTP Request Defaults 节点移动到 HTTP 请求节点之前。
每个请求节点可设置一个具有业务含义的名字,方便理解和管理。访问 JMeter 主页的 HTTP 请求可改名为 home ,同时 Path 修改为 / 。再添加一个 HTTP 请求节点,命名为 download page ,设置 Path 为 /download_jmeter.cgi 即可。
模拟在 home 页面停顿 1 秒钟。home 节点上右键,添加一个 Constant Timer 子节点,设置延迟时间为 1000 毫秒即可。
再次执行脚本,点击 View Results Tree 可看到两个 HTTP 请求节点的执行详情。
注意:
Timer 节点作为场景编排辅助节点,没有请求执行动作,也没有执行详情显示。
循环执行脚本时,最后一个节点 download page 执行结束后,会立即跳转到脚本开头,执行第一个节点 home 。
可在 download page 上也添加一个 Timer,模拟停留一秒之后再继续后续请求。
JMeter 的压测执行
编辑、调试脚本时,我们通常设置为 1 个线程并且只执行 1 次。执行压力测试时,通常需要以较高的压力持续执行一段时间。
脚本固定配置压力
如计划以 50 并发执行 2 分钟,可修改脚本 Thread Group 配置如下。
配置说明:
并发数(Number of Threads (users))设置为 50 。
循环次数(Loop Count)勾选永远执行(Forever)。
勾选 Scheduler,设置执行时长(Duration (seconds))为 120 秒。
通常我们在 JMeter 图形界面(GUI)编辑脚本,但执行压力测试时 GUI 占用额外资源可能影响施压性能,而且施压机可能没有图形界面环境(如 ssh 远程登录施压机)。因此脚本编辑完成后,通常以命令行模式执行 JMeter 压力测试。
进入 JMeter 脚本目录,执行 JMeter 压力测试的命令为:
jmeter -n -t <脚本>
如执行上述 test.jmx 脚本,命令如下:
jmeter -n -t test.jmx
输出结果如下:
Creating summariser <summary>
Created the tree successfully using test.jmx
Starting the test @ Tue Jun 25 14:38:32 CST 2019 (1561444712414)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary + 553 in 00:00:27 = 20.3/s Avg: 1378 Min: 252 Max: 8587 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary + 882 in 00:00:30 = 29.4/s Avg: 685 Min: 222 Max: 4272 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 1435 in 00:00:57 = 25.1/s Avg: 952 Min: 222 Max: 8587 Err: 0 (0.00%)
summary + 829 in 00:00:30 = 27.5/s Avg: 815 Min: 222 Max: 21310 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 2264 in 00:01:27 = 25.9/s Avg: 902 Min: 222 Max: 21310 Err: 0 (0.00%)
summary + 881 in 00:00:30 = 29.5/s Avg: 700 Min: 221 Max: 3896 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 3145 in 00:01:57 = 26.8/s Avg: 845 Min: 221 Max: 21310 Err: 0 (0.00%)
summary + 127 in 00:00:05 = 24.2/s Avg: 797 Min: 224 Max: 3819 Err: 0 (0.00%) Active: 0 Started: 50 Finished: 50
summary = 3272 in 00:02:02 = 26.7/s Avg: 843 Min: 221 Max: 21310 Err: 0 (0.00%)
Tidying up ... @ Tue Jun 25 14:40:35 CST 2019 (1561444835251)
... end of run
压测过程中默认每 30 秒输出一次统计数据,2 分钟后(实际为 00:02:02,比预设的 2 分钟多出少许误差)压测结束。看最后一行统计数据,平均 RPS(每秒请求数)为 26.7,平均 RT (响应时间)为 843 毫秒。
尝试核对一下统计数据。脚本包含两个请求,每个请求附加 1 秒钟等待时间,发送一个请求平均耗时为 RT 843 毫秒 + 等待 1000 毫秒。单线程理论 RPS 为 1000.0 / (843 + 1000),总共 50 个线程,全场景理论 RPS 为 1000.0 / (843 + 1000) * 50 = 27.13,与统计值 26.7 有一定误差。这是因为除了请求 RT 和等待时间,脚本执行请求之间还可能存在少量时间消耗。
命令行动态设置压力
实际工作中,常常需要以不同的压力大小反复执行压力测试,在脚本中写死压力大小(并发数)和执行时间显然很不方便。
如何动态指定压力大小呢,这里有一个技巧。JMeter 脚本支持使用 JMeter 属性进行配置,JMeter 命令行支持使用 -J 参数动态指定 JMeter 属性。把这两者结合起来,即可实现在命令行通过 -J 参数动态设置压力大小。
修改 JMeter 脚本使用 JMeter 属性配置压力大小,配置如下:
配置说明:
并发数配置为 ${__P(load.concurrency,1)},循环次数取消勾选 Forever,配置为 ${__P(load.count,1)}。未设置对应的 JMeter 属性时,默认为 1 ,满足只执行 1 次以测试、调试脚本的需求。
执行时长配置为 ${__P(load.duration,60)},默认 1 分钟(60 秒)。
测试命令行直接执行脚本:
jmeter -n -t test.jmx
可看到统计输出如下:
summary = 2 in 00:00:03 = 0.6/s Avg: 485 Min: 408 Max: 563 Err: 0 (0.00%)
默认一个并发并且只执行一次,发出 2 个请求(脚本循环一次发出两个请求),约 3 秒后脚本停止。注意:默认执行时间是 1 分钟,同时配置了循环次数和执行时间时,有一个条件先满足脚本即停止。
为了按指定时长执行,需要将执行次数设置为 Forever 。在 JMeter 内部实现中,执行次数为 -1 即表示 Forever 。指定以 50 并发执行 2 分钟,jmeter 命令行如下:
jmeter -n -t test.jmx -Jload.concurrency=50 -Jload.duration=120 -Jload.count=-1
执行结果与前述脚本固定配置 50 并发的结果类似。
云上的 JMeter 实践
阿里巴巴有着非常丰富的业务形态,每一种业务形态背后都由一系列分布式的技术体系提供服务,随着业务的快速发展,特别是在双11等大促营销等活动场景下,准确评估整个业务站点的服务能力成为一大技术难题。
在这个过程中,我们打造了自己的全链路压测系统,以应对更复杂、更多样的压测需求,并将此技术输出到 性能测试PTS 上,同时支持原生 JMeter 压测。
打开 PTS 控制台 主页,左侧导航栏选择 创建压测 > JMeter 压测 ,新建 JMeter 压测场景。填写场景名,如 jmeter-test 。场景配置 页面点击 上传文件 按钮,上传本地测试通过的 test.jmx 脚本。
施压配置 页面,并发数设置为 50,压测时长设置为 2 分钟。
点击 保存去压测,弹出提示框点击 确认,PTS 即开始在云端引擎执行 JMeter 脚本发起压力。
压测中页面如下:
压测中实时展示场景(及每个请求页面)实时 RPS 和 RT 等信息。可看到场景并发数为 50,RPS 为 26,RT 为 812 毫秒,与本地压测的结果差不多。注意:因为机器配置和网络环境的差异(PTS 施压机默认为 4核 8G,BGP多线路公网),PTS 上压测结果可能与本地压测结果存在一定差异。
针对 JMeter 施压配置,再补充几点说明:
PTS 上的施压配置会覆盖原脚本中的配置。原脚本无论是写死固定配置还是使用 JMeter 属性配置都没关系。
循环次数表示每个线程循环执行脚本的次数,可能与用户的直觉理解不一样,如 总请求数 = 脚本执行一次的请求数 * 循环次数 * 并发数 。
循环次数与预热时间同时配置时可能导致意外行为(如不能达到最大并发),因此 PTS 上不允许同时配置预热时间和循环次数(即只有预热时间为 0 时才允许设置循环次数)。
压测监控分析
性能测试不仅仅是简单的发起压力,对压力负载(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 采取了一个折中的办法,施压引擎每 5 秒对每个业务 API(压测Sampler)分别采样记录一条成功和失败(如果有)的请求详细信息。在压测中或压测报告页面,点击 查看采样日志 按钮即可查询记录的请求采样信息,并支持按业务 API(压测Sampler),响应状态(是否成功),请求时间等进行搜索过滤。
点击 查看详情 即可看到单个请求的详细信息。目前对详细信息提供了通用和 HTTP 两种展示模板,HTTP 展示模板可针对 HTTP 请求进行更友好的排版展示,展示内容包括请求 URL,请求方法,返回码,完整的请求头、请求体,响应头、响应体等。
因为页面上只展示文本内容,请求体或响应体包含图片等无法识别为文本的内容时,可能显示为乱码。另外当请求体或响应体很大时,对应的内容可能被截断。
JMeter 日志
本地执行 JMeter 脚本时,默认将日志记录到 jmeter.log 文件。在 PTS 上执行 JMeter 脚本时,可通过 JMeter日志 页面实时查看 JMeter 日志,并支持根据日志级别、时间或线程名进行查询过滤。
JMeter 日志主要用于脚本执行报错时排查错误原因。一些插件可能通过 JMeter 日志输出一些重要信息,用户在 groovy 脚本等代码中也可以直接打印日志。
日志打印过于频繁时,不仅可读性极差(大量重复日志淹没重要信息),而且影响 JMeter 性能,对 PTS 采集存储 JMeter 日志造成额外开销。因此 PTS 在采集 JMeter 日志时默认进行了限流,每秒钟打印日志条数超过 10 条时部分日志可能会丢失。良好设计的 JMeter 脚本应避免大量打印重复日志。
同样,良好设计的 JMeter 脚本应避免通过标准输出(System.out)或标准错误(System.err)打印输出信息,需要输出查看的重要信息应使用 JMeter 日志输出(如 groovy 脚本中使用 log.info("") )。PTS 上不支持查看 JMeter 脚本执行产生的标准输出和标准错误内容。
报告总结
压测结束后,PTS 将汇总监控数据形成压测报告。用户根据压测报告分析评估系统性能是否符合要求,如 RPS,成功率和 RT(响应时间)是否符合期望。并可辅助用户排查分析业务系统性能瓶颈。
PTS 压测报告页面可查询历史压测报告列表。
点击 查看报告 打开查看报告详情。压测报告在 PTS 上默认保存 30 天,可点击 报告导出 按钮,导出保存 PDF 版压测报告到本地。压测报告概要信息包括压测执行时间,RPS,RT,成功率等概要数据。场景详情包含全场景维度和业务 API 维度的监控统计信息。注意:受 JMeter 引擎本身统计功能的限制,仅全场景维度包含并发数统计。
此外,全场景维度和业务 API 维度均包含 RPS,成功率,网络带宽等统计。如 home 请求相关监控趋势图如下。
相比手动命令行执行 JMeter 脚本,PTS 更加 简单易用 ,提供 简单直观的监控 ,并提供 海量施压能力 。
免费 开通 PTS 服务,购买 5000 VU以上资源包,即可使用 JMeter 压测。PTS 计费单位是 VUM ,即并发(VU)* 分钟(最低消费 100 VUM)。上述场景消费为 50 并发 * 2 分钟 = 100 VUM 。如购买 5000 VU资源包,包含 10 万 VUM,售价 ¥278 ,上述计费可换算为 (100 / 10万) * ¥278 = ¥0.278 。
使用 CSV 参数文件
上述 JMeter 脚本仅简单请求固定 URL ,真实业务 API 通常带有请求参数。
JMeter 中可使用 CSV Data Set Config 读取 CSV 数据文件,简单实现请求参数化。CSV 文件默认首行为变量名(列名),其余行为 CSV 数据。准备一个 user.csv 文件,包含 id , name 两列,内容如下:
id,name
1,ali
2,pts
3,jmeter
注意:手工编辑 CSV 文件容易出错,推荐使用 Excel、Numbers 等软件导出,或编程使用 apache commons-csv 生成。编辑 JMeter 脚本,右键单击 Thread Group 添加一个 CSV Data Set Config 节点。
配置如下:
其中有两个地方需要注意(其他配置保持默认即可):
Filename 配置为文件名 user.csv 即可,不要包含文件路径。不同施压机上 CSV 文件路径可能不一样,只使用文件名(并在当前路径下执行脚本)以便兼容不同的施压机环境。
Sharing mode 设置为 Current thread group,指定 CSV 文件只被当前线程组使用。
假设 home 请求需要设置参数,配置请求参数直接使用 ${id} ,${name} 引用对应的变量即可。
测试执行脚本,查看 View Results Tree ,可看到执行请求时即会带上请求参数。
在阿里云 PTS 上执行脚本,只需编辑 JMeter 场景,上传修改后的 test.jmx 脚本文件和 user.csv 数据文件,点击 保存去压测 即可。
查看请求采样,可看到请求 URL 已添加对应的参数。
使用 JMeter 插件和附加 jar 包
JMeter 社区提供了丰富的插件,用户还可以自由添加使用 Java 库 Jar 包。在 PTS 上执行脚本时,只需要将额外添加的插件和 jar 包一起上传到 PTS 上,PTS 执行 JMeter 脚本时即可自动加载这些 Jar 包。详情可参考 如何进行 WebSocket 协议的压测 。
如果遇到问题,请先确认本地已测试通过,并确认额外使用的 Jar 包已全部上传到 PTS 。
海量施压能力
之前的脚本中,我们在请求后添加了 1 秒钟的等待时间以演示业务场景编排。如果压测场景前后请求没有关联,从服务器的角度看只是不停的收到各种请求,与客户端是否等待无关,因此 JMeter 脚本可去掉这些等待时间,以最大压力向服务器发起极限压测。
JMeter 使用并发(线程)数控制压力大小,通常(并发较小时)线程数越多压力越大。但单台施压机的能力毕竟有限,单机线程数增加到某个阈值时请求压力即达到极限。显然,为了继续增加压力,必须使用多台施压机进行分布式压测。
分布式压测需要注意以下几点:
每台施压机需要安装、启动 JMeter,拷贝分发脚本、CSV 数据、附加 jar 包等文件。
协调分配施压并发数。通常施压机配置相同,均分施压并发数即可。
统一控制启动、停止压测,统一收集聚合监控数据。
希望 CSV 数据唯一或打散数据时,多台施压机需要拆分 CSV 数据文件 。
使用阿里云 PTS 执行 JMeter 脚本时,用户只需要设置期望的最大并发即可 ,PTS 自动透明处理了分布式压测的问题,用户不用关心是单机压测还是分布式压测。PTS 默认每台 JMeter 施压机最大分配 500 并发,场景并发数超过 500 时即自动分配多台施压机。如果希望多台施压机拆分 CSV 数据文件,只要在上传 CSV 文件后勾选 切分文件 即可。详情参考 切分 CSV 数据文件 。
单机线程数极限与 JMeter 脚本的复杂程度和资源占用(如 CPU,内存,网络带宽占用等)情况有关。占用内存过高的 JMeter 脚本在并发过高时甚至可能会因内存溢出而导致 JMeter 引擎异常退出。对有特殊需求的高级用户,PTS 允许用户手动指定施压机数量,以更精确的控制总并发数和单机并发数。只需在 施压配置 页面勾选 指定IP数,设置相应的施压机数量即可。每台施压机有一个IP地址,指定IP数即指定施压机数量。
注意:指定IP数后,压测执行时独占了指定数量的施压机,每台施压机将按 500 并发计费(无论实际并发多少)。此功能专为有特殊需求的高级用户提供,普通用户通常不需要使用。
本文作者:韩勇,阿里云 PTS 技术专家。
深入Java微服务之网关系列1:什么是网关
前言近来,在想着重构一个新的产品。准备采用微服务的技术解决方案,来搭建基础设施框架。网关,是一个必不可少的组件。那么,网关到底是什么?其又有什么特点或者特性,成为微服务必不可少的组件呢?今天,我们就来探讨下这个问题。希望通过本文,大家能够明白,为何用。演变过程传统的单体技术架构,所有的内容,被打包进一个包内。为了保证,系统的稳定、安全,需要开发一些过滤器、拦截器,来实现对客户端请求的过滤与拦截,以及完成最终请求的转发。如下图所示微服务技术解决方案下,同样需要为每个服务开发过滤器、拦截器来进行请求管理。但由于服务数量众多,同时,客户端形式多样化,如果在每个服务身上开发,将会造成很大的代码冗余与开发负担。因此,期待,将相同的一些功能,抽取到一个服务内实现,这便成为了一个组件,就是现在的网关。网关存在的原因:解决微服务技术架构下,请求管理功能解决微服务技术架构下,多客户端的适配,采用单一入口,完成协议适配网关的基本功能微服务技术解决方案下的,网关,至少需要具备图示基本功能。网关作为单点入口,完成统一的请求管理免去客户端直接对接众多微服务的复杂性,采用单点入口,实现路由转发,从而实现服务调用服务对于整个系统来讲,是不稳定的,那么网关,需要进行限流熔断,保持系统的稳定与分区容错性对于服务调用的链路,网关有职责进行记录,日志监控,保证整个系统,在监控下工作系统可能不仅仅是由自有客户端调用,很多时候,系统开放能力API给外部,因此网关需要安全认证,来保证安全这些年来,API网关正在经历一些身份危机。它们是否是集中的、共享的资源,从而促进了API对外部实体的暴露与治理?它们是集群入口(ingress)哨兵,从而可以严格控制哪些用户流量进入或离开集群吗?或者它们根据自己拥有的客户端类型,使用某种API结合胶水来更简洁地表达API?当然,房间里的大象和我经常听到的一个问题:“服务网格会使API网关过时吗?房间里的大象:英语习语,指的是一些虽然显而易见,但却由于可能造成尴尬、争执、触及敏感或禁忌等原因被人刻意忽视的事情。一些背景随着技术发展日新月异,整个行业通过技术和架构模式进行快速洗牌,如果你说“所有这些都使我头大”,也可以理解。在本文中,我希望总结出“ API网关”的不同身份,阐明公司中的哪些群体可以使用API网关(他们正在尝试解决的问题),并重新关注这些首要原则。理想情况下,在本文结束时,您将更好地了解API基础架构在不同层级、对不同团队的作用,同时明白如何从每个层级获得最大价值。在深入探讨之前,让我们先明确API一词的含义。我对API的定义:一个明确定义和目的型接口,通过网络调用,使软件开发人员能够以受控且方便的方式,对组织内的数据和功能进行编程访问。这些接口抽象了实现它们的技术架构细节。对于这些设计的网络端点,我们希望获得一定程度的文档、使用指南、稳定性和向后兼容性。相反,仅仅因为我们可以通过网络与另一软件进行通信,并不一定意味着远程端点就是符合此定义的API。许多系统相互通信,但是通信发生更加随意,并在与耦合和其他因素之间进行权衡。我们创建API来为业务的各个部分提供周全的抽象,以实现新的业务功能以及偶然的创新。在谈论API网关时,首先要提到的是API管理。API管理许多人从API管理的角度考虑API网关。这是公平的。但是,让我们快速看一下此类网关的功能。通过API Management,我们试图解决“何时公开现有的API供他人使用”的问题,如何跟踪谁使用这些API,实施关于允许谁使用它们的政策,建立安全流程来进行身份验证和授权许可,同时创建一个服务目录(该目录可在设计时使用以促进API使用,并为有效治理奠定基础)。我们想解决“我们拥有要与他人共享,但要按我们的条款共享这些现有的、经过精心设计的API ”的问题。API管理也做得很好,它允许用户(潜在的API使用者)进行自助服务,签署不同的API使用计划(请考虑:在给定时间范围内,在指定价格点上,每个端点每个用户的调用次数)。有能力完成这些管理功能的基础架构就是网关(API流量所经过的)。在网关层,我们可以执行身份验证,速率限制,指标收集,其它策略执行等操作。API Management Gateway基于API网关的API管理软件示例:Google Cloud ApigeeRed Hat 3ScaleMulesoftKong在这个级别上,我们考虑的是API(如上定义)是如何最好地管理和允许对其进行访问。我们不是在考虑服务器,主机、端口、容器甚至服务(另一个定义不明确的词)。API管理(以及它们相应的网关)通常被作为由“平台团队”、“集成团队”或其它API基础架构团队所拥有的、严格控制的共享基础架构。需要注意的一件事:我们要小心,别让任何业务逻辑进入这一层。如前一段所述,API管理是共享的基础结构,但是由于我们的API流量经过了它,因此它倾向于重新创建“大包大揽的全能型”(认为是企业服务总线)网关,这会导致我们必须与之协调来更改我们的服务。从理论上讲,这听起来不错。实际上,这最终可能成为组织的瓶颈。有关更多信息,请参见这篇文章:具有ESB,API管理和Now…Service Mesh的应用程序网络功能?集群入口为了构建和实现API,我们将重点放在代码、数据、生产力框架等方面。但是,要想使这些事情中的任何一个产生价值,就必须对其进行测试,部署到生产中并进行监控。当我们开始部署到云原生平台时,我们开始考虑部署、容器、服务、主机、端口等,并构建可在此环境中运行的应用程序。我们可能正在设计工作流(CI)和管道(CD),以利用云平台快速迁移、更改、将其展示在客户面前等等。在这种环境中,我们可能会构建和维护多个集群来承载我们的应用程序,并且需要某种方式来访问这些群集中的应用程序和服务。以Kubernetes为例思考。我们可能会通过Kubernetes Ingress来访问Kubernetes集群(集群中的其它所有内容都无法从外部访问)。这样,我们就可以通过定义明确的入口点(例如域/虚拟主机、端口、协议等),严格控制哪些流量可以进入(甚至离开)我们的集群。在这个级别上,我们可能希望某种“ingress网关”成为允许请求和消息进入群集的流量哨兵。在这个级别上,您的思考更多是“我的集群中有此服务,我需要集群外的人能够调用它”。这可能是服务(公开API)、现有的整体组件、gRPC服务,缓存、消息队列、数据库等。有些人选择将其称为API网关,而且实际上可能会做比流量的入口/出口更多的事情,但重点是这个层级的问题是属于集群操作级别的。Cluster Ingress Gateway这些类型的ingress实现的示例包括:Envoy Proxy 及其基础上的项目包括:Datawire AmbassadorSolo.io GlooHeptio Contour基于其他反向代理/负载均衡器构建的其它组件:HAProxyOpenShift’s Router (based on HAProxy)NGINXTraefikKong此层级的集群入口控制器由平台团队操作,但是,这部分基础架构通常与更加去中心化的、自助服务工作流相关联(正如您对云原生平台所期望的那样)。参见The “GitOps” workflow as described by the good folks at WeaveworksAPI网关模式关于“ API网关”一词的另一种扩展是我在听到该术语时通常想到的,它是与API网关模式最相似的。Chris Richardson在其“微服务模式”一书第8章很好地介绍了这种用法。我强烈建议您将此书用于此模式和其他微服务模式学习资料。可在他的microservices.io网站API Gatway Pattern可以进行快速浏览。简而言之,API网关模式是针对不同类别的消费者来优化API的使用。这个优化涉及一个API间接访问。您可能会听到另一个代表API网关模式的术语是“前端的后端”,其中“前端”可以是字符终端(UI)、移动客户端、IoT客户端甚至其它服务/应用程序开发人员。在API网关模式中,我们明显简化了一组API的调用,以模拟针对特定用户、客户端或使用者的“应用程序”内聚API。回想一下,当我们使用微服务构建系统时,“应用程序”的概念就消失了。API网关模式有助于恢复此概念。这里的关键是API网关,一旦实现,它将成为客户端和应用程序的API,并负责与任何后端API和其他应用程序网络端点(不满足上述API定义的端点)进行通信。与上一节中的Ingress控制器不同,此API网关更接近开发人员的视角,而较少关注哪些端口或服务暴露以供集群外使用的方面。此“ API网关”也不同于我们管理现有API的API管理视角。此API网关将对后端的调用聚合在一起,这可能会公开API,但也可能是与API描述较少的东西,例如对旧系统的RPC调用,使用不符合“ REST”的协议的调用(如通过HTTP但不使用JSON),gRPC,SOAP,GraphQL、websockets和消息队列。这种类型的网关也可用来进行消息级转换、复杂的路由、网络弹性/回退以及响应的聚合。如果您熟悉REST API的Richardson Maturity模型,就会发现相比Level 1–3,实现了API网关模式的API网关来集成了更多的Level 0请求(及其之间的所有内容)。https://martinfowler.com/articles/richardsonMaturityModel.html这些类型的网关实现仍需要解决速率限制、身份验证/授权、断路、度量收集、流量路由等问题。这些类型的网关可以在集群边缘用作集群入口控制器,也可以在集群内部用作应用程序网关。API Gateway Pattern此类API网关的示例包括:Spring Cloud GatewaySolo.io GlooNetflix ZuulIBM-Strongloop Loopback/Microgateway也可以使用更通用的编程或集成语言/框架(例如:Apache CamelSpring IntegrationBallerina.ioEclipse Vert.xNodeJS由于这种类型的API网关与应用和服务的开发紧密相关,因此我们希望开发人员能够参与帮助指定API网关公开的API,了解所涉及的任何聚合逻辑以及快速测试和更改此API基础架构的能力。我们还希望运维人员或SRE对API网关的安全性、弹性和可观察性配置有一些意见。这种层级的基础架构还必须适应不断发展的、按需的、自助服务开发人员工作流。可以通过查看GitOps模型获取更多这方面信息。进入服务网格(Service Mesh)在云基础架构上运行服务架构的一部分难点是,如何在网络中构建正确级别的可观察性和控制。在解决此问题的先前迭代中,我们使用了应用程序库和希望的开发人员治理来实现此目的。但是,在大规模和多种开发语言环境下,服务网格技术的出现提供了更好的解决方案。服务网格通过透明地实现为平台及其组成服务带来以下功能:服务到服务(即东西向流量)的弹性安全性包括最终用户身份验证、相互TLS、服务到服务RBAC / ABAC黑盒服务的可观察性(专注于网络通信),例如请求/秒、请求延迟、请求失败、熔断事件、分布式跟踪等服务到服务速率限制,配额执行等精明的读者会认识到,API网关和服务网格在功能上似乎有所重叠。服务网格的目的是通过在L7透明地解决所有服务/应用程序的这些问题。换句话说,服务网格希望融合到服务中(实际上它的代码并没有嵌入到服务中)。另一方面,API网关位于服务网格以及应用程序之上(L8?)。服务网格为服务、主机、端口、协议等(东西向流量)之间的请求流带来了价值。它们还可以提供基本的集群入口功能,以将某些此功能引入南北向。但是,这不应与API网关可以带来北/南流量的功能相混淆。(一个在集群的南北向和一个是在一组应用程序的南北向)Service Mesh和API网关在某些方面在功能上重叠,但是在它们在不同层面互补,分别负责解决不同的问题。理想的解决方案是将每个组件(API管理、API网关、服务网格)合适的安置到您的解决方案中,并根据需要在各组件间建立良好的边界(或在不需要时排除它们)。同样重要的是找到适合去中心化开发人员和运营工作流程的这些工具实现。即使这些不同组件的术语和标识存在混淆,我们也应该依靠基本原理,并了解这些组件在我们的体系结构中带来的价值,从而来确定它们如何独立存在和互补并存。微服务不能没有网关,就如同 Java 程序员不能没有IDEA、Eclipse。为什么呢?之所以网关对微服务这么重要,主要有以下几点原因:1. 解决 API 放哪里的问题要知道,采用微服务架构的系统本身是由很多的独立服务单元组合起来的。而客户端要调用系统,则必须通过系统提供的各种对外开放的 API 来实现。问题来了,这些 API 要放在哪里呢?直接放在组成系统的服务单元上行不行?比如,在一套电商系统上,关于订单相关的 API ,放在组成订单服务的服务单元上;风控服务的 API ,放在组成风控服务的服务单元上。好,咱们假设有这么一个场景,有一位用户想在这套电商系统上查看下商品详情。那么,这个查看商品详情的操作,就可能:调用商品服务的 API 获取商品描述调用评价服务的 API 获取相关评价调用商家服务的 API 获取商家信息调用礼券服务的 API 获取相关礼券……可以看到,就这么一个商品查看操作,就可能会调用许多服务的 API。那这些 API 如果全部分散到各个服务单元上,供客户端调用,像查看商品这么简单的一次操作,客户端就可能需要远程访问好几次甚至十几次服务器。微服务自己又讲究把 API 的粒度划分的很细,也就是说,可能从商品服务上调用商品信息,不止是调用一次商品服务就够了,很可能需要多次对商品服务的不同 API 进行调用,才能获取到足够的数据。这样一来,客户端需要访问服务器的次数就更多了,可能十几次都不够,得几十次。这种多次访问服务器的行为,会极大延迟客户端的界面响应时间,很不现实。所以,把 API 放到各个业务相关的服务单元上,看上去问题很大。那为什么引入网关就能解决这个问题呢?因为引入网关,就相当于在客户端和微服务之间加了一层隔离。通常,网关本身会和各个服务单元处于同一个机房,这样,客户端做业务操作的时候,只需要访问一次网关。然后剩下的事情,再由网关分别访问同在一个机房的不同的服务,再把拿到的数据统一在网关封装好,返回给客户端就好。2. 解决边缘功能集成的问题在一套微服务组成的系统里,除了必须的业务功能以外,还有为了系统自身的健壮与安全,以及微服务本身的管理,而必须引入的一些非业务功能。对于这些非业务又很重要的功能,我们统称为边缘功能。还是拿电商系统为例,我们来看一些重要的边缘功能。假设因为我们做了一次非常大的促销活动,导致流量过大,系统承载不了了。此时,为了保证系统本身的稳定,我们就需要把一些承载不了的流量给通过各种手段消化掉,一般的做法有三种:限流:通过令牌桶等算法,把一些额外的流量挡在系统外面,不让其访问。降级:由于系统可能已经过载了,此时,我们就放弃处理一些服务和页面的请求或者仅简单处理,比如直接返回一个报错。熔断:有些时候,系统过载过度或者上线出了 bug,降级都解决不了问题。比如,缓存失效了,导致大量请求频繁访问了数据库,而这种频繁访问数据库可能造成了大量的 IO 操作,结果又去影响了数据库所在的操作系统,同时,这个操作系统上又有着别的重要服务,直接也被影响了。对于这种连锁反应,我们称之为雪崩。而为了防止雪崩,我们就会坚决把缓存失效导致数据库被频繁访问的服务给停掉,这就是熔断。可以看到,像限流、降级、熔断这些系统保障策略,最合适的地方应该是有一个集中的请求入口点,就像古时候,老百姓进城需要过城门那样。当系统出现问题的时候,直接就在这个入口点做相应的操作即可。限流,就直接在这个入口点限制后续请求。降级,就直接在这个入口点判断请求想要访问的服务或者页面,直接报错返回。熔断,就直接在这个入口点,断开所有访问特定服务的请求连接,然后再把后继对特定服务的访问,也统统拦在门外。在电商系统里,有很多特殊场景的接口,需要受到严格的限制。比如,支付接口,访问它就需要认证和权限控制。又比如,对于系统的访问,有时候不能让国外的去访问国内的网站,这就需要限制客户端的访问 IP,所以系统还需要认证和授权功能。那这种认证和授权也最合适放在请求的一个集中入口点,统一实现。还记得上面咱们说过的网关的 API 统一存放吗?我们只需要对这些 API 做对应的权限设置,当请求访问特殊场景接口的时候,必定会通过 API 访问。所以,限制接口的访问,本质上就是对特定 API 的限制,那么,放在网关再合适不过了。现实里,我们有时候需要把线上的流量镜像出来,转发到灰度环境,利用这些镜像出来的流量既可以用于小范围测试,又可以更好的评估系统所能承载的最大吞吐量,也因此,系统需要有一个统一入口做分流。可以看到,无论是系统需要的保障策略,认证授权,还是流量分流等功能,都应该放到一个统一的请求入口处才能得到最好的实现。网关恰好就承担了这么个统一请求入口的角色。所以,对于微服务中,林林总总的边缘功能,往往会通过插件的形式,集成在 API 网关中。3. 解耦了客户端和后端微服务一套项目,在使用微服务模式的初期,往往后端变化是十分频繁的。频繁变化的原因有很多,像业务领域划分不合适啊,像某个业务模块急速膨胀啊,都可能导致后端微服务的剧烈变化。在这种情况下,如果没有网关,很可能就会出现客户端需要被迫随着后端的变化而变化的情况。比如,在电商系统里,初期我们很可能会把风控服务做的非常小。随着业务的发展,风控服务越来越庞大,此时,风控服务就可能被分解为决策引擎和分析中心等更多更细的服务。在电商里,风控往往是下单、支付等操作的必要前置操作。如果没有网关去分隔开客户端和微服务,客户端直接和风控服务打交道,那么风控服务拆分,API 必然不会稳定,API 的变化,自然会引发调用 API 客户端代码的变化。有了网关之后,情况就好了很多了。当风控服务本身频繁变化的时候,我们只需要改动网关的代码就好。而服务器代码的升级可是远远要比客户端代码的升级容易太多了。参考链接:https://juejin.cn/post/6918211351257022471为什么微服务一定要有API网关?jianshu.com/p/9fab0982c6bb
工作几年了,API 网关还不懂?
翻译一篇API网关的文章,介绍了其三种角色:API管理、集群ingress网关、API网关模式,最后还讲了与service mesh的关系,通过此文可以更全面的理解API网关的作用。原文:API Gateways are going through an identity crisis这些年来,API网关正在经历一些身份危机。它们是否是集中的、共享的资源,从而促进了API对外部实体的暴露与治理?它们是集群入口(ingress)哨兵,从而可以严格控制哪些用户流量进入或离开集群吗?或者它们根据自己拥有的客户端类型,使用某种API结合胶水来更简洁地表达API?当然,房间里的大象和我经常听到的一个问题:“服务网格会使API网关过时吗?房间里的大象:英语习语,指的是一些虽然显而易见,但却由于可能造成尴尬、争执、触及敏感或禁忌等原因被人刻意忽视的事情。一些背景随着技术发展日新月异,整个行业通过技术和架构模式进行快速洗牌,如果你说“所有这些都使我头大”,也可以理解。在本文中,我希望总结出“ API网关”的不同身份,阐明公司中的哪些群体可以使用API网关(他们正在尝试解决的问题),并重新关注这些首要原则。理想情况下,在本文结束时,您将更好地了解API基础架构在不同层级、对不同团队的作用,同时明白如何从每个层级获得最大价值。在深入探讨之前,让我们先明确API一词的含义。我对API的定义:一个明确定义和目的型接口,通过网络调用,使软件开发人员能够以受控且方便的方式,对组织内的数据和功能进行编程访问。这些接口抽象了实现它们的技术架构细节。对于这些设计的网络端点,我们希望获得一定程度的文档、使用指南、稳定性和向后兼容性。相反,仅仅因为我们可以通过网络与另一软件进行通信,并不一定意味着远程端点就是符合此定义的API。许多系统相互通信,但是通信发生更加随意,并在与耦合和其他因素之间进行权衡。我们创建API来为业务的各个部分提供周全的抽象,以实现新的业务功能以及偶然的创新。在谈论API网关时,首先要提到的是API管理。API管理许多人从API管理的角度考虑API网关。这是公平的。但是,让我们快速看一下此类网关的功能。通过API Management,我们试图解决“何时公开现有的API供他人使用”的问题,如何跟踪谁使用这些API,实施关于允许谁使用它们的政策,建立安全流程来进行身份验证和授权许可,同时创建一个服务目录(该目录可在设计时使用以促进API使用,并为有效治理奠定基础)。我们想解决“我们拥有要与他人共享,但要按我们的条款共享这些现有的、经过精心设计的API ”的问题。API管理也做得很好,它允许用户(潜在的API使用者)进行自助服务,签署不同的API使用计划(请考虑:在给定时间范围内,在指定价格点上,每个端点每个用户的调用次数)。有能力完成这些管理功能的基础架构就是网关(API流量所经过的)。在网关层,我们可以执行身份验证,速率限制,指标收集,其它策略执行等操作。API Management Gateway基于API网关的API管理软件示例:Google Cloud ApigeeRed Hat 3ScaleMulesoftKong在这个级别上,我们考虑的是API(如上定义)是如何最好地管理和允许对其进行访问。我们不是在考虑服务器,主机、端口、容器甚至服务(另一个定义不明确的词)。API管理(以及它们相应的网关)通常被作为由“平台团队”、“集成团队”或其它API基础架构团队所拥有的、严格控制的共享基础架构。需要注意的一件事:我们要小心,别让任何业务逻辑进入这一层。如前一段所述,API管理是共享的基础结构,但是由于我们的API流量经过了它,因此它倾向于重新创建“大包大揽的全能型”(认为是企业服务总线)网关,这会导致我们必须与之协调来更改我们的服务。从理论上讲,这听起来不错。实际上,这最终可能成为组织的瓶颈。有关更多信息,请参见这篇文章:具有ESB,API管理和Now…Service Mesh的应用程序网络功能?集群入口为了构建和实现API,我们将重点放在代码、数据、生产力框架等方面。但是,要想使这些事情中的任何一个产生价值,就必须对其进行测试,部署到生产中并进行监控。当我们开始部署到云原生平台时,我们开始考虑部署、容器、服务、主机、端口等,并构建可在此环境中运行的应用程序。我们可能正在设计工作流(CI)和管道(CD),以利用云平台快速迁移、更改、将其展示在客户面前等等。在这种环境中,我们可能会构建和维护多个集群来承载我们的应用程序,并且需要某种方式来访问这些群集中的应用程序和服务。以Kubernetes为例思考。我们可能会通过Kubernetes Ingress来访问Kubernetes集群(集群中的其它所有内容都无法从外部访问)。这样,我们就可以通过定义明确的入口点(例如域/虚拟主机、端口、协议等),严格控制哪些流量可以进入(甚至离开)我们的集群。在这个级别上,我们可能希望某种“ingress网关”成为允许请求和消息进入群集的流量哨兵。在这个级别上,您的思考更多是“我的集群中有此服务,我需要集群外的人能够调用它”。这可能是服务(公开API)、现有的整体组件、gRPC服务,缓存、消息队列、数据库等。有些人选择将其称为API网关,而且实际上可能会做比流量的入口/出口更多的事情,但重点是这个层级的问题是属于集群操作级别的。Cluster Ingress Gateway这些类型的ingress实现的示例包括:Envoy Proxy 及其基础上的项目包括:Datawire AmbassadorSolo.io GlooHeptio Contour基于其他反向代理/负载均衡器构建的其它组件:HAProxyOpenShift’s Router (based on HAProxy)NGINXTraefikKong此层级的集群入口控制器由平台团队操作,但是,这部分基础架构通常与更加去中心化的、自助服务工作流相关联(正如您对云原生平台所期望的那样)。参见The “GitOps” workflow as described by the good folks at WeaveworksAPI网关模式关于“ API网关”一词的另一种扩展是我在听到该术语时通常想到的,它是与API网关模式最相似的。Chris Richardson在其“微服务模式”一书第8章很好地介绍了这种用法。我强烈建议您将此书用于此模式和其他微服务模式学习资料。可在他的microservices.io网站API Gatway Pattern可以进行快速浏览。简而言之,API网关模式是针对不同类别的消费者来优化API的使用。这个优化涉及一个API间接访问。您可能会听到另一个代表API网关模式的术语是“前端的后端”,其中“前端”可以是字符终端(UI)、移动客户端、IoT客户端甚至其它服务/应用程序开发人员。在API网关模式中,我们明显简化了一组API的调用,以模拟针对特定用户、客户端或使用者的“应用程序”内聚API。回想一下,当我们使用微服务构建系统时,“应用程序”的概念就消失了。API网关模式有助于恢复此概念。这里的关键是API网关,一旦实现,它将成为客户端和应用程序的API,并负责与任何后端API和其他应用程序网络端点(不满足上述API定义的端点)进行通信。与上一节中的Ingress控制器不同,此API网关更接近开发人员的视角,而较少关注哪些端口或服务暴露以供集群外使用的方面。此“ API网关”也不同于我们管理现有API的API管理视角。此API网关将对后端的调用聚合在一起,这可能会公开API,但也可能是与API描述较少的东西,例如对旧系统的RPC调用,使用不符合“ REST”的协议的调用(如通过HTTP但不使用JSON),gRPC,SOAP,GraphQL、websockets和消息队列。这种类型的网关也可用来进行消息级转换、复杂的路由、网络弹性/回退以及响应的聚合。如果您熟悉REST API的Richardson Maturity模型,就会发现相比Level 1–3,实现了API网关模式的API网关来集成了更多的Level 0请求(及其之间的所有内容)。https://martinfowler.com/articles/richardsonMaturityModel.html这些类型的网关实现仍需要解决速率限制、身份验证/授权、断路、度量收集、流量路由等问题。这些类型的网关可以在集群边缘用作集群入口控制器,也可以在集群内部用作应用程序网关。API Gateway Pattern此类API网关的示例包括:Spring Cloud GatewaySolo.io GlooNetflix ZuulIBM-Strongloop Loopback/Microgateway也可以使用更通用的编程或集成语言/框架(例如:Apache CamelSpring IntegrationBallerina.ioEclipse Vert.xNodeJS由于这种类型的API网关与应用和服务的开发紧密相关,因此我们希望开发人员能够参与帮助指定API网关公开的API,了解所涉及的任何聚合逻辑以及快速测试和更改此API基础架构的能力。我们还希望运维人员或SRE对API网关的安全性、弹性和可观察性配置有一些意见。这种层级的基础架构还必须适应不断发展的、按需的、自助服务开发人员工作流。可以通过查看GitOps模型获取更多这方面信息。进入服务网格(Service Mesh)在云基础架构上运行服务架构的一部分难点是,如何在网络中构建正确级别的可观察性和控制。在解决此问题的先前迭代中,我们使用了应用程序库和希望的开发人员治理来实现此目的。但是,在大规模和多种开发语言环境下,服务网格技术的出现提供了更好的解决方案。服务网格通过透明地实现为平台及其组成服务带来以下功能:服务到服务(即东西向流量)的弹性安全性包括最终用户身份验证、相互TLS、服务到服务RBAC / ABAC黑盒服务的可观察性(专注于网络通信),例如请求/秒、请求延迟、请求失败、熔断事件、分布式跟踪等服务到服务速率限制,配额执行等精明的读者会认识到,API网关和服务网格在功能上似乎有所重叠。服务网格的目的是通过在L7透明地解决所有服务/应用程序的这些问题。换句话说,服务网格希望融合到服务中(实际上它的代码并没有嵌入到服务中)。另一方面,API网关位于服务网格以及应用程序之上(L8?)。服务网格为服务、主机、端口、协议等(东西向流量)之间的请求流带来了价值。它们还可以提供基本的集群入口功能,以将某些此功能引入南北向。但是,这不应与API网关可以带来北/南流量的功能相混淆。(一个在集群的南北向和一个是在一组应用程序的南北向)Service Mesh和API网关在某些方面在功能上重叠,但是在它们在不同层面互补,分别负责解决不同的问题。理想的解决方案是将每个组件(API管理、API网关、服务网格)合适的安置到您的解决方案中,并根据需要在各组件间建立良好的边界(或在不需要时排除它们)。同样重要的是找到适合去中心化开发人员和运营工作流程的这些工具实现。即使这些不同组件的术语和标识存在混淆,我们也应该依靠基本原理,并了解这些组件在我们的体系结构中带来的价值,从而来确定它们如何独立存在和互补并存。
阿里巴巴在开源压测工具 JMeter 上的实践和优化
Apache JMeter 是 Apache 旗下的开源压测工具,创建于 1999 年初,迄今已有超过 20 年历史。JMeter 功能丰富,社区(用户群体)庞大,是主流开源压测工具之一。性能测试通常集中在新系统上线或大型活动前(如电商大促,春节活动等),以验证系统能力,帮助排查定位性能瓶颈等问题。一次压测活动可粗略分为几个步骤:场景配置。配置压测场景模拟用户(业务)与系统的交互。压测执行。按指定压力量级启动压测。压测监控分析。压测中通常关注施压 RPS,成功率,业务响应时间(RT),网络带宽等关键指标。报告总结。披露系统能力是否符合要求,同时沉淀记录系统性能演变和优化过程。下面我们讨论如何使用 JMeter 完成上述步骤,及相关的最佳实践建议。JMeter 使用 Java 开发,需要先安装 JDK 并配置好 PATH 环境变量,然后从官网下载 JMeter 二进制压缩包解压即可。建议将 JMeter bin 目录也添加到 PATH 环境变量,这样在命令行下输入 jmeter 命令即可启动 JMeter 。一、场景配置简单 HTTP 请求配置最常见的压测场景即 HTTP 压测。压测场景在 JMeter 脚本中叫做 Test Plan(压测计划),打开 JMeter 默认即为一个空 Test Plan 。JMeter 使用并发(线程)数控制压力大小,一个线程可看做一个执行请求的虚拟用户。在 Test Plan 上点击右键,添加一个 Thread Group(线程组)。线程组默认为 1 个线程并只执行一次 1 次,这很方便测试执行脚本,保持此默认值即可。JMeter 中发送请求的组件叫做 Sampler(采样器)。在 Thread Group 上单击右键,添加一个 HTTP Request 节点(采样器)。HTTP 请求最关键的配置即 URL,JMeter 允许将 URL 协议类型(Protocol)、服务器名、请求路径(Path)等拆开单独配置。也可以直接将整个 URL(如 JMeter 主页 http://jmeter.apache.org/ )填写到 Path,其他字段保留为空即可。这样,一个最简单的 HTTP 压测脚本就配置好了。为了方便测试、调试脚本,可在 Test Plan 下添加一个 View Results Tree 监听器(Listener)。这个监听器仅用于编辑脚本时测试、调试脚本,查看请求执行详情,不需要做任何配置。测试执行脚本第一次执行脚本前,需要先保存脚本,如保存为 test.jmx 。以后每次执行脚本前,JMeter 默认会自动保存脚本。连续多次执行脚本时,JMeter 默认不会清理历史记录。为了避免历史执行结果干扰,可先点击 Clear All 按钮手动清空历史记录,再点击 Start 按钮执行脚本,这样看到的执行结果更清爽,方便排查问题。按照默认线程组配置,脚本执行一次即结束。点击 View Results Tree ,可看到请求执行详细信息,包括请求头,请求体,响应头和完整的响应体等信息。场景编排真实压测场景通常不会只有一个请求,而是多个请求按一定顺序和规则的编排组合,即场景编排。场景编排是 JMeter 等压测引擎最重要的功能之一,也是与 apache ab等简单压测工具的重要区别之一。这里我们假设一个最简单的场景,先访问 JMeter 主页,停留 1 秒钟后跳转到下载页。一个脚本访问一个网站的不同页面(Path)时,可添加一个 HTTP Request Defaults 节点,配置默认协议类型和服务器名。这样可避免重复配置,需要修改协议类型(如 https 与 http 切换)或压测域名时,只用修改 HTTP Request Defaults 即可。HTTP Request Defaults 配置服务器名为 jmeter.apache.org(协议类型默认为 http),鼠标可拖动 HTTP Request Defaults 节点移动到 HTTP 请求节点之前。每个请求节点可设置一个具有业务含义的名字,方便理解和管理。访问 JMeter 主页的 HTTP 请求可改名为 home ,同时 Path 修改为 / 。再添加一个 HTTP 请求节点,命名为 download page ,设置 Path 为 /download_jmeter.cgi 即可。模拟在 home 页面停顿 1 秒钟。home 节点上右键,添加一个 Constant Timer 子节点,设置延迟时间为 1000 毫秒即可。再次执行脚本,点击 View Results Tree 可看到两个 HTTP 请求节点的执行详情。注意:Timer 节点作为场景编排辅助节点,没有请求执行动作,也没有执行详情显示。循环执行脚本时,最后一个节点 download page 执行结束后,会立即跳转到脚本开头,执行第一个节点 home 。可在 download page 上也添加一个 Timer,模拟停留一秒之后再继续后续请求。二、JMeter 的压测执行编辑、调试脚本时,我们通常设置为 1 个线程并且只执行 1 次。执行压力测试时,通常需要以较高的压力持续执行一段时间。脚本固定配置压力如计划以 50 并发执行 2 分钟,可修改脚本 Thread Group 配置如下。配置说明:并发数(Number of Threads (users))设置为 50 。循环次数(Loop Count)勾选永远执行(Forever)。勾选 Scheduler,设置执行时长(Duration (seconds))为 120 秒。通常我们在 JMeter 图形界面(GUI)编辑脚本,但执行压力测试时 GUI 占用额外资源可能影响施压性能,而且施压机可能没有图形界面环境(如 ssh 远程登录施压机)。因此脚本编辑完成后,通常以命令行模式执行 JMeter 压力测试。进入 JMeter 脚本目录,执行 JMeter 压力测试的命令为:jmeter -n -t <脚本>如执行上述 test.jmx 脚本,命令如下:jmeter -n -t test.jmx输出结果如下:Creating summariser <summary>
Created the tree successfully using test.jmx
Starting the test @ Tue Jun 25 14:38:32 CST 2019 (1561444712414)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary + 553 in 00:00:27 = 20.3/s Avg: 1378 Min: 252 Max: 8587 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary + 882 in 00:00:30 = 29.4/s Avg: 685 Min: 222 Max: 4272 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 1435 in 00:00:57 = 25.1/s Avg: 952 Min: 222 Max: 8587 Err: 0 (0.00%)
summary + 829 in 00:00:30 = 27.5/s Avg: 815 Min: 222 Max: 21310 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 2264 in 00:01:27 = 25.9/s Avg: 902 Min: 222 Max: 21310 Err: 0 (0.00%)
summary + 881 in 00:00:30 = 29.5/s Avg: 700 Min: 221 Max: 3896 Err: 0 (0.00%) Active: 50 Started: 50 Finished: 0
summary = 3145 in 00:01:57 = 26.8/s Avg: 845 Min: 221 Max: 21310 Err: 0 (0.00%)
summary + 127 in 00:00:05 = 24.2/s Avg: 797 Min: 224 Max: 3819 Err: 0 (0.00%) Active: 0 Started: 50 Finished: 50
summary = 3272 in 00:02:02 = 26.7/s Avg: 843 Min: 221 Max: 21310 Err: 0 (0.00%)
Tidying up ... @ Tue Jun 25 14:40:35 CST 2019 (1561444835251)
... end of run压测过程中默认每 30 秒输出一次统计数据,2 分钟后(实际为 00:02:02,比预设的 2 分钟多出少许误差)压测结束。看最后一行统计数据,平均 RPS(每秒请求数)为 26.7,平均 RT (响应时间)为 843 毫秒。尝试核对一下统计数据。脚本包含两个请求,每个请求附加 1 秒钟等待时间,发送一个请求平均耗时为 RT 843 毫秒 + 等待 1000 毫秒。单线程理论 RPS 为 1000.0 / (843 + 1000),总共 50 个线程,全场景理论 RPS 为 1000.0 / (843 + 1000) * 50 = 27.13,与统计值 26.7 有一定误差。这是因为除了请求 RT 和等待时间,脚本执行请求之间还可能存在少量时间消耗。命令行动态设置压力实际工作中,常常需要以不同的压力大小反复执行压力测试,在脚本中写死压力大小(并发数)和执行时间显然很不方便。如何动态指定压力大小呢,这里有一个技巧。JMeter 脚本支持使用 JMeter 属性进行配置,JMeter 命令行支持使用 -J 参数动态指定 JMeter 属性。把这两者结合起来,即可实现在命令行通过 -J参数动态设置压力大小。修改 JMeter 脚本使用 JMeter 属性配置压力大小,配置如下:配置说明:并发数配置为 ${__P(load.concurrency,1)},循环次数取消勾选 Forever,配置为 ${__P(load.count,1)}。未设置对应的 JMeter 属性时,默认为 1 ,满足只执行 1 次以测试、调试脚本的需求。执行时长配置为 ${__P(load.duration,60)},默认 1 分钟(60 秒)。测试命令行直接执行脚本:jmeter -n -t test.jmx可看到统计输出如下:summary = 2 in 00:00:03 = 0.6/s Avg: 485 Min: 408 Max: 563 Err: 0 (0.00%)默认一个并发并且只执行一次,发出 2 个请求(脚本循环一次发出两个请求),约 3 秒后脚本停止。注意:默认执行时间是 1 分钟,同时配置了循环次数和执行时间时,有一个条件先满足脚本即停止。为了按指定时长执行,需要将执行次数设置为 Forever 。在 JMeter 内部实现中,执行次数为 -1 即表示 Forever 。指定以 50 并发执行 2 分钟,jmeter 命令行如下:jmeter -n -t test.jmx -Jload.concurrency=50 -Jload.duration=120 -Jload.count=-1执行结果与前述脚本固定配置 50 并发的结果类似。三、云上的 JMeter 实践阿里巴巴有着非常丰富的业务形态,每一种业务形态背后都由一系列分布式的技术体系提供服务,随着业务的快速发展,特别是在双11等大促营销等活动场景下,准确评估整个业务站点的服务能力成为一大技术难题。在这个过程中,我们打造了自己的全链路压测系统,以应对更复杂、更多样的压测需求,并将此技术输出到 性能测试PTS 上,同时支持原生 JMeter 压测。打开 PTS 控制台 主页,左侧导航栏选择 创建压测 > JMeter 压测 ,新建 JMeter 压测场景。填写场景名,如 jmeter-test 。场景配置 页面点击 上传文件 按钮,上传本地测试通过的 test.jmx 脚本。施压配置 页面,并发数设置为 50,压测时长设置为 2 分钟。点击 保存去压测,弹出提示框点击 确认,PTS 即开始在云端引擎执行 JMeter 脚本发起压力。压测中页面如下:压测中实时展示场景(及每个请求页面)实时 RPS 和 RT 等信息。可看到场景并发数为 50,RPS 为 26,RT 为 812 毫秒,与本地压测的结果差不多。注意:因为机器配置和网络环境的差异(PTS 施压机默认为 4核 8G,BGP多线路公网),PTS 上压测结果可能与本地压测结果存在一定差异。针对 JMeter 施压配置,再补充几点说明:PTS 上的施压配置会覆盖原脚本中的配置。原脚本无论是写死固定配置还是使用 JMeter 属性配置都没关系。循环次数表示每个线程循环执行脚本的次数,可能与用户的直觉理解不一样,如 总请求数 = 脚本执行一次的请求数 * 循环次数 * 并发数 。循环次数与预热时间同时配置时可能导致意外行为(如不能达到最大并发),因此 PTS 上不允许同时配置预热时间和循环次数(即只有预热时间为 0 时才允许设置循环次数)。四、压测监控分析性能测试不仅仅是简单的发起压力,对压力负载(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 采取了一个折中的办法,施压引擎每 5 秒对每个业务 API(压测Sampler)分别采样记录一条成功和失败(如果有)的请求详细信息。在压测中或压测报告页面,点击 查看采样日志 按钮即可查询记录的请求采样信息,并支持按业务 API(压测Sampler),响应状态(是否成功),请求时间等进行搜索过滤。点击 查看详情 即可看到单个请求的详细信息。目前对详细信息提供了通用和 HTTP 两种展示模板,HTTP 展示模板可针对 HTTP 请求进行更友好的排版展示,展示内容包括请求 URL,请求方法,返回码,完整的请求头、请求体,响应头、响应体等。因为页面上只展示文本内容,请求体或响应体包含图片等无法识别为文本的内容时,可能显示为乱码。另外当请求体或响应体很大时,对应的内容可能被截断。JMeter 日志本地执行 JMeter 脚本时,默认将日志记录到 jmeter.log 文件。在 PTS 上执行 JMeter 脚本时,可通过 JMeter日志 页面实时查看 JMeter 日志,并支持根据日志级别、时间或线程名进行查询过滤。JMeter 日志主要用于脚本执行报错时排查错误原因。一些插件可能通过 JMeter 日志输出一些重要信息,用户在 groovy 脚本等代码中也可以直接打印日志。日志打印过于频繁时,不仅可读性极差(大量重复日志淹没重要信息),而且影响 JMeter 性能,对 PTS 采集存储 JMeter 日志造成额外开销。因此 PTS 在采集 JMeter 日志时默认进行了限流,每秒钟打印日志条数超过 10 条时部分日志可能会丢失。良好设计的 JMeter 脚本应避免大量打印重复日志。同样,良好设计的 JMeter 脚本应避免通过标准输出(System.out)或标准错误(System.err)打印输出信息,需要输出查看的重要信息应使用 JMeter 日志输出(如 groovy 脚本中使用 log.info("") )。PTS 上不支持查看 JMeter 脚本执行产生的标准输出和标准错误内容。五、报告总结压测结束后,PTS 将汇总监控数据形成压测报告。用户根据压测报告分析评估系统性能是否符合要求,如 RPS,成功率和 RT(响应时间)是否符合期望。并可辅助用户排查分析业务系统性能瓶颈。PTS 压测报告页面可查询历史压测报告列表。点击 查看报告 打开查看报告详情。压测报告在 PTS 上默认保存 30 天,可点击 报告导出 按钮,导出保存 PDF 版压测报告到本地。压测报告概要信息包括压测执行时间,RPS,RT,成功率等概要数据。场景详情包含全场景维度和业务 API 维度的监控统计信息。注意:受 JMeter 引擎本身统计功能的限制,仅全场景维度包含并发数统计。此外,全场景维度和业务 API 维度均包含 RPS,成功率,网络带宽等统计。如 home 请求相关监控趋势图如下。相比手动命令行执行 JMeter 脚本,PTS 更加 简单易用 ,提供 简单直观的监控 ,并提供 海量施压能力 。免费 开通 PTS 服务,购买 5000 VU以上资源包,即可使用 JMeter 压测。PTS 计费单位是 VUM ,即并发(VU)* 分钟(最低消费 100 VUM)。上述场景消费为 50 并发 * 2 分钟 = 100 VUM 。如购买 5000 VU资源包,包含 10 万 VUM,售价 ¥278 ,上述计费可换算为 (100 / 10万) * ¥278 = ¥0.278 。六、使用 CSV 参数文件上述 JMeter 脚本仅简单请求固定 URL ,真实业务 API 通常带有请求参数。JMeter 中可使用 CSV Data Set Config 读取 CSV 数据文件,简单实现请求参数化。CSV 文件默认首行为变量名(列名),其余行为 CSV 数据。准备一个 user.csv 文件,包含 id , name 两列,内容如下:id,name
1,ali
2,pts
3,jmeter注意:手工编辑 CSV 文件容易出错,推荐使用 Excel、Numbers 等软件导出,或编程使用 apache commons-csv 生成。编辑 JMeter 脚本,右键单击 Thread Group 添加一个 CSV Data Set Config节点。配置如下:其中有两个地方需要注意(其他配置保持默认即可):Filename 配置为文件名 user.csv 即可,不要包含文件路径。不同施压机上 CSV 文件路径可能不一样,只使用文件名(并在当前路径下执行脚本)以便兼容不同的施压机环境。Sharing mode 设置为 Current thread group,指定 CSV 文件只被当前线程组使用。假设 home 请求需要设置参数,配置请求参数直接使用 ${id} ,${name} 引用对应的变量即可。测试执行脚本,查看 View Results Tree ,可看到执行请求时即会带上请求参数。在阿里云 PTS 上执行脚本,只需编辑 JMeter 场景,上传修改后的 test.jmx 脚本文件和 user.csv数据文件,点击 保存去压测 即可。查看请求采样,可看到请求 URL 已添加对应的参数。使用 JMeter 插件和附加 jar 包JMeter 社区提供了丰富的插件,用户还可以自由添加使用 Java 库 Jar 包。在 PTS 上执行脚本时,只需要将额外添加的插件和 jar 包一起上传到 PTS 上,PTS 执行 JMeter 脚本时即可自动加载这些 Jar 包。详情可参考 如何进行 WebSocket 协议的压测 。如果遇到问题,请先确认本地已测试通过,并确认额外使用的 Jar 包已全部上传到 PTS 。七、海量施压能力之前的脚本中,我们在请求后添加了 1 秒钟的等待时间以演示业务场景编排。如果压测场景前后请求没有关联,从服务器的角度看只是不停的收到各种请求,与客户端是否等待无关,因此 JMeter 脚本可去掉这些等待时间,以最大压力向服务器发起极限压测。JMeter 使用并发(线程)数控制压力大小,通常(并发较小时)线程数越多压力越大。但单台施压机的能力毕竟有限,单机线程数增加到某个阈值时请求压力即达到极限。显然,为了继续增加压力,必须使用多台施压机进行分布式压测。分布式压测需要注意以下几点:每台施压机需要安装、启动 JMeter,拷贝分发脚本、CSV 数据、附加 jar 包等文件。协调分配施压并发数。通常施压机配置相同,均分施压并发数即可。统一控制启动、停止压测,统一收集聚合监控数据。希望 CSV 数据唯一或打散数据时,多台施压机需要拆分 CSV 数据文件 。使用阿里云 PTS 执行 JMeter 脚本时,用户只需要设置期望的最大并发即可 ,PTS 自动透明处理了分布式压测的问题,用户不用关心是单机压测还是分布式压测。PTS 默认每台 JMeter 施压机最大分配 500 并发,场景并发数超过 500 时即自动分配多台施压机。如果希望多台施压机拆分 CSV 数据文件,只要在上传 CSV 文件后勾选 切分文件 即可。详情参考 切分 CSV 数据文件 。单机线程数极限与 JMeter 脚本的复杂程度和资源占用(如 CPU,内存,网络带宽占用等)情况有关。占用内存过高的 JMeter 脚本在并发过高时甚至可能会因内存溢出而导致 JMeter 引擎异常退出。对有特殊需求的高级用户,PTS 允许用户手动指定施压机数量,以更精确的控制总并发数和单机并发数。只需在 施压配置 页面勾选 指定IP数,设置相应的施压机数量即可。每台施压机有一个IP地址,指定IP数即指定施压机数量。注意:指定IP数后,压测执行时独占了指定数量的施压机,每台施压机将按 500 并发计费(无论实际并发多少)。此功能专为有特殊需求的高级用户提供,普通用户通常不需要使用。
云原生时代如何用 Prometheus 实现性能压测可观测-Metrics 篇
作者:拂衣什么是性能压测可观测可观测性包括 Metrics、Traces、Logs3 个维度。可观测能力帮助我们在复杂的分布式系统中快速排查、定位问题,是分布式系统中必不可少的运维工具。在性能压测领域中,可观测能力更为重要,除了有助于定位性能问题,其中Metrics性能指标更直接决定了压测是否通过,对系统上线有决定性左右,具体如下:Metrics,监控指标系统性能指标,包括请求成功率、系统吞吐量、响应时长资源性能指标,衡量系统软硬件资源使用情况,配合系统性能指标,观察系统资源水位Logs,日志施压引擎日志,观察施压引擎是否健康,压测脚本执行是否有报错采样日志,采样记录 API 的请求和响应详情,辅助排查压测过程中的一些出错请求的参数是否正常,并通过响应详情,查看完整的错误信息Traces,分布式链路追踪用于性能问题诊断阶段,通过追踪请求在系统中的调用链路,定位报错 API 的报错系统和报错堆栈,快速定位性能问题点本篇阐述如何使用 Prometheus 实现性能压测 Metrics 的可观测性。 压测监控的核心指标系统性能指标压测监控最重要的 3 个指标:请求成功率、服务吞吐量(TPS)、请求响应时长(RT),这 3 个指标任意一个出现拐点,都可以认为系统已达到性能瓶颈。这里特别说明下响应时长,对于这个指标,用平均值来判断很有误导性,因为一个系统的响应时长并不是平均分布的,往往会出现长尾现象,表现为一部分用户请求的响应时间特别长,但整体平均响应时间符合预期,这样其实是影响了一部分用户的体验,不应该判断为测试通过。因此对于响应时长,常用 99、95、90 分位值来判断系统响应时长是否达标。另外,如果需要观察请求响应时长的分布细节,可以补充请求建联时长(Connect Time)、等待响应时长(Idle Time)等指标。资源性能指标压测过程中,对系统硬件、中间件、数据库资源的监控也很重要,包括但不限于:CPU 使用率内存使用率磁盘吞吐量网络吞吐量数据库连接数缓存命中率... ...详细可见《测试指标》[1]一文。施压机性能指标压测链路中,施压机性能是容易被忽略的一环,为了保证施压机不是整个压测链路的性能瓶颈,需要关注如下施压机性能指标:压测进程的内存使用量施压机 CPU 使用率,Load1、Load5 负载指标基于 JVM 的压测引擎,需要关注垃圾回收次数、垃圾回收时长 为什么用 Prometheus 做压测监控开源压测工具如 JMeter 本身支持简单的系统性能监控指标,如:请求成功率、系统吞吐量、响应时长等。但是对于大规模分布式压测来说,开源压测工具的原生监控有如下不足:监控指标不够全面,一般只包含了基础的系统性能指标,只能用于判断压测是否通过。但是如果压测不通过,需要排查、定位问题时,如分析一个 API 的 99 分位建联时长,原生监控指标就无法实现。聚合时效性不能保证无法支持大规模分布式的监控数据聚合监控指标不支持按时间轴回溯综上,在大规模分布式压测中,不推荐使用开源压测工具的原生监控。下面对比 2 种开源的监控方案:方案一:ZabbixZabbix 是早期开源的分布式监控系统,支持 MySQL 或 PostgreSQL 关系型数据库作为数据源。对于系统性能监控,需要施压机提供秒级的监控指标,每秒高并发的监控指标写入,使关系型数据库成为了监控系统的瓶颈。对于资源性能监控,Zabbix 对物理机、虚拟机的指标很全面,但是对容器、弹性计算的监控支持还不够。方案二:PrometheusPrometheus 使用时序数据库作为数据源,相比传统关系型数据库,读写性能大大提高,对于施压机大量的秒级监控数据上报的场景,性能表现良好。对于资源性能监控,Prometheus 更适用于云资源的监控,尤其对 Kubernates 和容器的监控非常全面,对使用云原生技术的用户,上手更简单。总结下来,Prometheus 相较 Zabbix,更适合于压测中高并发监控指标的采集和聚合,并且更适用于云资源的监控,且易于扩展。当然,使用成熟的云产品也是一个很好选择,如压测工具 PTS[2]+可观测工具 ARMS[3],就是一组黄金搭档。PTS 提供压测时的系统性能指标,ARMS 提供资源监控和整体可观测的能力,一站式解决压测可观测的问题。怎么使用 Prometheus 实现压测监控开源 JMeter 改造Prometheus 是拉数据模型,因此需要压测引擎暴露 HTTP 服务,供 Prometheus 获取各压测指标。JMeter 提供了插件机制,可以自定义插件来扩展 Prometheus 监控能力。在自定插件中,需要扩展 JMeter 的 BackendListener,让在采样器执行完成时,更新每个压测指标,如成功请求数、失败请求数、请求响应时长。并将各压测指标在内存中保存,在 Prometheus 拉数据时,通过 HTTP 服务暴露出去。整体结构如下:JMeter 自定义插件需要改造的点:增加指标注册中心扩展 Prometheus 指标更新器实现自定义 JMeter BackendListener,在采样器执行结束后,调用 Prometheus 更新器实现 HTTP Server,如果有安全需要,补充鉴权逻辑 PTS 压测工具性能测试 PTS(Performance Testing Service)是一款阿里云 SaaS 化的性能测试工具。PTS支持自研压测引擎,同时支持开源 JMeter 压测,在 PTS 上开放压测指标到 Prometheus,无需开发自定义插件来改造引擎,只需 3 步白屏化操作即可。具体步骤如下:PTS 压测的高级设置中,打开【Prometheus】开关压测开始后,在【监控导出】一键复制 Prometheus 配置自建的 Prometheus 中粘贴并热加载此配置,即可生效详细参考:《如何将 PTS 压测的指标数据输出到 Prometheus》[4]快速搭建 Grafana 监控大盘PTS 提供了官方 Grafana 大盘模板[5],支持一键导入监控大盘,并可以灵活编辑和扩展,满足您的定制监控需求。本大盘提供了全局请求成功率,系统吞吐量(TPS),99、95、90 分位响应时长,以及按错误状态码聚合的错误请求数等数据。在 API 分布专栏中,可以直观的对比各 API 的监控指标,快速定位性能短板 API。在 API 详情专栏中,可以查看单个 API 的详细指标,准确定位性能瓶颈。另外,大盘还提供了施压机的JVM垃圾回收监控指标,可以辅助判断施压机是否是压测链路中的性能瓶颈。导入步骤如下:步骤一在菜单栏,点击 Dashboard 下的 import:步骤二填写 PTS Dashboard 的 id:15981在 Prometheus 选择您已有的数据源,本示例中数据源名为 Prometheus。选中后,单击 Import 导入步骤三导入后,在左上角【PTS 压测任务】,选择需要监控的压测任务,即可看到当前监控大盘。此任务名对应 PTS 控制台在监控导出-Prometheus 配置中的 jobname。总结本文阐述了什么是性能测试可观测为什么用 Prometheus 做压测性能指标监控如何使用开源 JMeter 和云上 PTS 实现基于 Prometheus 的压测监控PTS 压测监控导出 Prometheus 功能,目前免费公测中,欢迎使用。同时,PTS 全新售卖方式来袭,基础版价格直降 50%!百万并发价格只需 6200!更有新用户 0.99 体验版、VPC 压测专属版,欢迎大家选购!相关链接[1] 测试指标https://help.aliyun.com/document_detail/29338.html[2] 压测工具 PTShttps://www.aliyun.com/product/pts[3] 可观测工具 ARMShttps://www.aliyun.com/product/arms[4] 如何将 PTS 压测的指标数据输出到 Prometheushttps://help.aliyun.com/document_detail/416784.html[5]官方 Grafana 大盘模板https://grafana.com/grafana/dashboards/15981