详解站点压测利器——nGrinder
导语
本文主要介绍nGrinder这款强大的压测工具,一起了解下它的架构原理、环境搭建和压测流程,为我们提供稳定易扩展的分布式压测环境。希望对大家有所帮助和启发。
背景
下面将主要介绍nGrinder这款强大的压测工具,一起了解下它的原理特点、环境搭建步骤和压测流程,为我们提供稳定易扩展的分布式压测环境。
站点压测利器——nGrinder
jmeter是一款大家熟知的压测软件,它是apache组织开发的一款基于java的压测软件,而nGrinder所为一款分布式高并发开源压测软件,是由NHN公司基于Grinder开源项目所做的二次开发,那么nGrinder与Jemter之间的对比有什么不同呢?
2. nGrinder整体架构
nGrinde压测框架核心由controller和agent两个组件组成,其中controller负责管理和控制测试流程(web管理后台)、下发测试任务&脚本文件到agent端以及查看测试监控报告等。agent负责启动压测进程&线程,压测目标服务器,并在测试完成后归还agent给AgentManager管理,还可以监控目标服务器&被压测站点各项性能指标(基于jmx)。
使用nGrinder开启一个压测任务前,首先需要部署controller和多个agent端,agent端在启动后会链接到controller内的AgentManager(Agent管理池)。之后需要我们通过controller端的web管理界面来创建压测脚本和测试任务,这时我们就可以执行已创建好的压测任务了。整体执行流程如下:
当压测任务启动后,nGrinder首先会创建一个nGrinder Console;
已创建完成的nGrinder Console会向AgentManager申请本次压测任务所需数量的agent;
nGrinder Console会向申请到的agent派发本次压测任务所需的脚本、资源文件等内容;
资源分发完毕后,nGrinder Console会控制agent执行测试流程(按照压测任务中配置多个进程和线程来读取资源文件&并发执行压测脚本,模拟多用户(vuser)并发压测站点),直到本次测试结束;
最后我们还可以在controller管理后台查看测试报告&借助Monitor组件监控系统性能指标。
nGrinder的整体结构图如下:
3. 环境搭建
3.1 下载nGrinder
下载地址:https://github.com/naver/ngrinder/releases选择最新版本ngrinder-3.4(即grinder-controller-3.4.war)
3.2 使用tomcat启动controller
3.4版本ngrinder支持在jdk1.8环境下运行,使用tomcat8.0版本部署即可,将grinder-controller-3.4.war放置在tomcat容器的/webApps文件夹中 ,或将文件按独立web工程解压,部署在如/opt/web/ngrinder/webapps目录下。成功启动后,访问站点ip+端口号,进入登陆页面,如下图所示:
Controller安装完成后,登陆管理后台站点,点击顶部右侧下拉菜单中的Download Agent进行下载。
将下载好的ngrinder-agent-3.4.tar直接解压,执行目录下的sh run_agent.sh & 命令即完成agent启动。如果需要停止agent,执行stop_agent.sh。启动agent.sh后,可以点击右上角菜单中的Agents Management选项进入agents管理页面。
3.4 安装monitor
1> 点击右上角菜单中的Download Monitor选项
2> 下载monitor,将monitor安装包解压在被压测机器。执行目录下的启动命令:sh run_monitor.sh即可
3> 至此压测环境基本搭建完成,通过管理后台即可查看部署的agent和monitor节点是否和controller端连接成功。
需要注意的是:请勿将agent和被压测站点部署在同一台机器上(会严重影响被压测站点性能,争抢cpu、内存等机器资源)。monitor节点需要和被压测站点部署在同一台机器上。
4. 编写压测脚本
4.1 简易创建压测脚本
压测站点前,首先需要编写压测脚本。通过点击管理后台上方的script按钮,进入脚本管理页面,即可创建。(在首页quick start输入框中输入目标url也可直接创建脚本,效果相同)
由于Groovy和java之间具有极高的兼容性,很多jdk类库可直接调用,很容易上手编写,对应java开发人员来说,学习成本很小。
4.2 使用脚本模拟真实稳定压测环境
通常线上web或者服务站点出于性能优化考量,针对各业务场景都大量使用到缓存,导致压测数据相同时,每次命中缓存后快速响应请求,造成站点压测性能极佳的假象(但线上真实请求参数各异)。所有站点做压测时,更希望让每个压测请求都携带不同的线上请求参数,为了满足模拟真实压测环境要求,就需要我们在脚本开发时做如下准备
1> 准备线上真实环境下各种参数形式的压测数据
2> 让每个压测线程,每次访问目标服务器时,分别携带不同的请求参数
3> 控制单个压测线程的单次执行时间,保证各线程能以稳定速率向目标服务器发送请求
4> 脚本是否能通过传参灵活控制内部逻辑
上述几个要求,是我们接下来重点考虑的问题
4.2.1 压测数据准备:以压测web站点为例,我们可以利用tomcat开启的access日志来收集线上真实请求数据(nginx的线上访问日志同样满足),将打印在access日志中的正常格式请求过滤出来,整理成压测数据文件。如果压测的是单台机器,还需要将数据中的域名替换为ip+端口形式,留待压测时使用。下图所示是某线上站点的请求数据:
注意:当压测依赖下游服务,尤其是其他部门站点时,需要评估对依赖方的影响并予以周知,避免压测数据影响线上业务&统计分析等
4.2.2 各压测线程读取不同数据:由于nGrinder是分布式压测工具,在向每个agent节点下发的测试数据文件都是相同的,这就需要我们在脚本中指定不同的数据文件片段供各线程分别读取。nGrinder为了方便我们让每个线程执行不同的业务逻辑,内置了ScriptContext上下文容器对象,供我们获取当前脚本运行环境下的常用参数,如下图所示:
由于脚本容器在启动时已将所有的agent、进程和线程进行了编号,从0到该类型节点在配置项中的最大值-1。例如当我们使用1个agent节点下配置10个process,每个process下配置100个线程来运行脚本,此时当前线程内获取到的grinder.processNumber值是0到9中的某一个,grinder.threadNumber线程值是0到99中的某一个,这样我们便可以先将整个数据文件按总agent、总进程和总线程数做数据切割,再根据当前线程所对应的进程、线程编号,读取对应的压测数据。如下是在进程初始化时,单个agent下对多进程、多线程压测数据文件切割示例:可先通过如下代码筛选出当前进程可读取的数据片段范围,后续线程执行时,再根据当前线程编号获取各自线程的数据片段,按顺序读取即可:
4.2.3脚本控制稳定的TPS:由于ngrinder在执行压测任务时,所有启动线程都在循环重复执行脚本,每次请求发送响应时间都是不同的,这种情况下单位时间内单个线程的执行次数是不可控的,TPS会随平均响应时间变慢而下降,无法维持稳定的TPS压测环境。针对这种情况,我们可以在压测脚本中增加Thread.sleep方法,控制单次脚本执行耗时,当并发执行该脚本时,使执行脚本的线程以稳定的时间间隔执行,产生稳定的TPS。
上图所示等待时间,为1秒减去当前请求耗时(等待时间=单位时间1秒 -当前请求执行耗时,这样用Thread.sleep(等待时间) 方法来控制等待时间,保证每个单位时间周期内,都能稳定发出一次请求,从编码上保证TPS的稳定性),当压测目标站点平均响应时间很长时(如平均超过1秒占比较大),可自行调整等待总时间到2000ms甚至更高的毫秒数,通过脚本逻辑保证单个线程产生的TPS是稳定的。
4.2.4 脚本传参方式:通过前面的介绍,了解到控制压测请求TPS需要获取整个环境下的总agent数、进程数和线程数,通过写死当前压测任务下的这些数值会使得每次调整压测Tps时都要去修改脚本,而nGrinder提供了脚本输入参数配置项,我们可以自定义输入配置来传递当前压测任务的总进程、线程数,脚本中使用System.getProperty("param")方法即可获取到输入参数:
PS:在调整参数过程中,如果所准备的压测数据过大时,建议增大进程数,从而减小单个进程所需提取的压测数据段大小,减少初始化时间。
5. 执行压测任务
5. 1 选择主页上面的Performance Test选项,进入性能测试列表页中,点击create Test创建压测任务,如下图所示:
在配置压测环境时,需要遵循如下规定
1> 配置项中的vuser虚拟用户数量直接对应脚本单位时间内的并发执行次数TPS(TPS = 执行请求数量 / 单位时间(秒)),也可通过设置进程、线程数量让平台自动计算所需初始化的进程&线程数,对应关系为vuser数= procsses数 * Thread数。(设置vuser数 或processes + Thread数 两种配置并发效果相同)
2> 单个procsses配置的线程数请勿超过200,单个agent支持的总vuser数建议不要超过5000,且保证单个agent运行至少有双核、6G空闲内存可使用(单agent并发量越高,所需内存越大)
3> 当被压集群qbs大于3000时,建议启动多个agent来分摊压测进程,来保证压测性能稳定性
4> 使用配置项paramter可向压测脚本中传入自定义参数,(如传入进程&线程数,来切分压测数据,让每个线程读取不同的压测数据,达到模拟线上真实请求的目的)
5.2 配置好压测任务后即可立刻执行压测任务(平台也提供了延迟执行压测任务功能)压测执行结果如下图所示:
我们可以看到,通过脚本内部控制单次请求执行&等待时间,使得整个压测任务在启动后TPS都较为平稳,我们还可以通过查看压测日志文件&详细压测报告(含CSV表格,如下图所示),观察整个压测过程中站点响应时间变化、机器性能状况(需要被压测机安装monitor)等。
6. 扩展功能介绍
6.1 支持依赖第三方包 & 压测数据文件获取:
nGrinder支持上传压测数据文件 & 第三方类库jar包,灵活支持自定义脚本的扩展性,只需在lib和resource目录中上传依赖的数据文件和jar包,在脚本里用使用相对路径配置对应目录即可,上传位置如下图所示:
6.2 自定义数据视图:
我们在使用nGrinder配置脚本压测时,除了观察基本的Tps、平均响应时间、机器cpu、内存等性能指标时,还可以通过压测脚本收集数据,展示在自定义视图上(只支持展示一种自定义数据),我们可以在脚本初始化时,使用grinder.statistics.registerSummaryExpression(java.lang.StringdisplayName, java.lang.String expression) 方法定义传入数据计算表达式,将每个线程执行时收集到的数据传入grinder.statistics.forLastTest方法中,最终汇总到controller端展示在最后的详细报告中。
下图是用自定义方法收集到的脚本逻辑平均耗时图:
总结、展望或规划
通过本文介绍,我们能看到nGrinder是一款性能稳定可扩展灵活的分布式压测工具,当然,这依赖于开发人员在压测脚本等所做的二次开发编码,我们可使用更贴近java语法的Groovy语言编码来降低我们的语言学习成本,更快速的编写出灵活通用的压测脚本,满足我们对站点吞吐量的准确评估需求,更好的规划线上站点的机器数配比,平稳度过全年流量高峰期。
参考文献
1. 官网资料地址:
https://github.com/naver/ngrinder/releases
2. api接口文档:
http://grinder.sourceforge.net/g3/script-javadoc/index.html?help-doc.html
作者简介
胡晔 / 58同城招聘技术部-主站与基础架构组 资深开发工程师,负责组内运营&seo、消息推送、电话保护、任务管理平台等业务与基础组件开发&维护。
END