【51CTO.com快译】众所周知,功能性测试通常着眼于单个用户对于目标应用程序的基本使用体验。而对成百上千的用户同时访问目标站点的场景,特别是那些面向公众服务的Web应用,开发人员势必需要在应用上线之前,模拟真实的并发访问情况,并通过考虑和进行负载测试,为任何可能发生的问题与意外,做好充分的准备。
在本指南中,我将通过如下四个阶段,向您介绍负载测试过程中的相关知识。
- 计划:为何要测试?测试什么?
- 编写测试脚本:如何测试?
- 执行:针对应用程序运行测试脚本。
- 分析和报告:发现了什么?需要修复什么?
计划负载测试
在计划阶段,团队会聚到一起,讨论并确定包括目标、范围、所需资源、以及测试场景等方面的内容。此阶段的可交付成果为测试计划。不过,它在形式上是否正式,则取决于团队的实际要求。值得注意的是,任何测试计划都是一项团队活动,我们需要从所有利益相关者(即:参与测试或可能受测试影响的任何人)处获取建议。
要求
我们需要搞清楚为什么要做负载测试?该问题看似简单,但是不同的利益相关者可能会给出不同的测试场景需求。因此为了达成一致,我们最好将负载测试的目标表述为非功能性的需求 (nonfunctional requirements,NFR),以便定义应用程序的期望状态,并且能够包括由团队决定的性能阈值。例如:基于响应时间的要求,已注册的用户登录用时不应超过3秒(即达到90%的响应时间)。可见,好的需求往往能够满足:SMART(具体的Specific、可衡量的Measurable、已商定的Agreed Upon、可实现的Realistic、以及及时的Timely)等特点。而且在实践中,我们可以从如下方面进行把控。
范围
约定了负载测试的共同目标,我们不但可以缩小待测内容的领域,还能确保每个成员都清楚有哪些部分不需要被测试。
先决条件
在开展测试之前,我们需要考虑包括:参与的人数、执行测试所需的时间、以及测试数据的环境要求等多方面的资源与先决条件。虽然我们并不总是需要一个单独的测试环境,来进行负载测试,但是如果需要的话,应该事先知会DevOps等团队。
负载建模
负载模型可以被用来描述模拟负载的一组特征。例如:用户最常访问的是哪些页面、哪个服务部分、最长访问的时段与地理位置等方面。您可以将这些因素植入测试脚本中,以更加真实地模拟出,可能对应用程序产生的流量和影响。
服务器监控
在测试过程中,我们往往需要通过脚本,来监控后台服务器上各类警报与日志。如果您的负载测试工具检测到明显的性能下降,则需要能够通过关联服务器上的相应信息,来予以勘察。不过,值得注意的是,任何类型的监控都会增加现有服务资源的开销,因此整个团队应事先确定好需要监控和跟踪的指标与信息。
测试场景
待测试和模拟的场景设定,往往能够协助我们更加准确地获取网站与应用的真实性能。例如:我们既可以创建一个典型工作日的负载配置文件,又可以模拟大促时的峰值负载配置文件,还可以模拟某个生产出现中断时的场景。不同的测试场景能够为我们采集到的诸如:持续时间、并发用户数、请求数、激增周期(即:应用从0个用户上升到最大用户量的速度)、以及“峰谷”负载(即:在整个测试过程中,每秒请求数的波动)等,可供分析的参数数据。
在设计负载测试计划时,您既可以包含多条用户故事线,又可以使用各种Trello卡片(译者注:一种卡片式任务管理工具)。工具的输出格式并不重要;重要的是每个成员都能够从中解读出一致性、规范化的测试结果。
编写测试脚本
有了在前面计划阶段的各项准备,现在我们便可以开始通过编写负载测试脚本,来为测试的实施做好准备了。
工具的选择
目前,市场上有着许多负载测试类工具。出于对各种在用工具、以及财务和供应商等方面的考量,我们不一定可以自由地按需选择负载测试工具,但是如下方面还是值得我们认真考虑的:
资源和成本
- 整体购置的价格,及其定价模型是否允许用户按需增减使用量?
- 实际需要自行构建,还是应当购买端到端的负载测试方案?
- 团队拥有哪些技术技能与工具经验?谁将负责进行测试?
特点
- 负载测试工具是否能够支持应用程序所使用的协议和技术?
- 测试工具与对应脚本语言的学习曲线是否“陡峭”?
- 测试工具是否可以让多人轻松地协作,并处理同一个脚本?
- 能否与测试栈中的其他可用于测试管理、测试结果分析、服务器监控、团队沟通等工具相集成?
- 测试工具能否被添加到CI/CD管道中?
- 该工具是否通过提供简单的方法,来设置性能阈值或服务等级目标(SLO)?
- 测试结果能否显示在可用的报告中,以及能否与现有的数据可视化工具相集成?
支持
- 如果出现问题,客户支持是否需要通过额外付费才能获取?
- 在线帮助文档是否够用?
- 是否拥有经验分享的用户社区?
扩展负载测试
- 单个负载生成器每秒可模拟多少个虚拟用户或请求?
- 能否处理由测试所产生的大数据量?
- 能否根据用户的位置特征,从不同的地理位置实施测试?
- 是否允许用户从云端生成负载?
鉴于上述需求,我选定k6(https://k6.io/)作为开发负载测试脚本的工具。总的说来,k6有着如下三方面的优势:
- 由于它是开源的,因此任何人都可以下载该工具,并亲自试用其脚本。
- 由于是用JavaScript编写而成,因此那些使用过Cypress、Puppeteer或Playwright等工具的前端开发人员、或测试人员都能够轻松地上手。
- 由于它能够在一台机器上生成上万个用户的并发数,因此即便在负载增加的情况下,它也具有一定的成本效益。
当然,下面将要讨论到的各个步骤也适用于其他工具。而且,我强烈建议您使用多种工具,自行进行概念验证,以找到最适合特定情况和测试场景的工具。
安装和设置
如果您使用的是Mac系统,那么便可以使用Brew来轻松地安装k6,即:从终端处运行--brew install k6,就能够在几秒钟之内搞定。当然,如果您使用的是其他操作系统的话,请通过链接--https://k6.io/docs/getting-started/installation/,来获取详细的安装说明。
一个基本的k6测试脚本
如下JavaScript代码段展示了最基本的k6测试脚本:
- import http from ‘k6/http’;
- export default function () {
- let response = http.get(‘https://test.k6.io’);
- }
该脚本会向一个简单的测试站点(其URL为https://test.k6.io)发送一个HTTP的GET请求。请将该代码复制到文本编辑器中,并存为test.js文件。接着,您便可以使用命令--k6run test.js,来运行它,并得到如下显示结果:
在k6中运行简单的本地测试示例
从上述截图中,我们可以看出,本地主器上的1个虚拟用户执行了1次脚本的迭代。而该HTTP测试请求的响应时间为128.2毫秒(即:http_req_duration)。
使测试脚本更加真实
显然,上述脚本的GET请求过于简单,无法达到用负载测试脚本在数量、以及地理分布上模拟真实用户的访问情况。而如果负载测试脚本不够真实,则会在测试期间产生误报,甚至无法让用户发现应用组件潜在的性能问题。下面,我们来讨论一下如何让测试脚本更为真实。
添加静态资源
如果页面上带有可被浏览器自动检索到的图像或脚本等静态资源,那么请确保测试脚本也会去检索此类资源。毕竟它们会影响到您获得页面的响应时间。当然,如果您已经获得了来自第三方提供商的许可,那么您也可以考虑从第三方处获取静态资源。
设置缓存和cookie行为
虽然站点的首次访问者无法调用本地的缓存资源,但是重复访问者则会从缓存中检索相应的资源。对此,我们应当检查负载测试工具的默认缓存和cookie管理的设置,并通过按需更改,来匹配真实的测试场景。
添加“思考时间”
真正的用户通常不会不停地反复刷新相同的URL。他们在导航到目标网站后,会花一些时间阅读页面上的内容,与页面上的某些组件进行交互。这些用户的“思考”时间其实就是各个请求之间的延迟。因此,通过添加1到5秒的“思考时间”,我们可以让应用服务器免于遭受外挂机器人发出的请求轰炸。
使用不同的用户路径
用户路径是指用户如何与应用程序进行交互的流程,其中包括:他们会访问哪些页面,输入哪些信息等。我们前面在计划阶段所建立的工作负载模型,可以直接反映出系统中最常见的用户流程,例如:使用不同的浏览器进行登录,或是从购物车中选择商品付款等。在k6中,我们可以通过添加不同的场景来实现,而其他工具则可能称之为线程组或执行组。
添加测试数据
正如普通用户是不会一遍又一遍地搜索相同的关键字,也不会使用同一个测试帐户在各处同时登录那样,我们的测试脚本也不应该重复地使用相同的数据。如果多个虚拟用户使用同一帐户进行登录,那么就可能导致其响应被缓存,进而缩短响应的时间,或增加错误率。对此,您需要将脚本修改为从某个CSV文件中获取测试数据,以增加真实的随机性。
设置测试参数
测试参数往往能够通过调整测试用户数的激增与骤降,来改变用户负载的时间变化趋势,进而协助您绘制出虚拟用户数与时间变化的曲线图表。
在k6中体现的负载测试期间虚拟用户的数量变化
设置失败的判定标准
模拟真实用户的一个重要环节便是发现有哪些因素会导致用户访问的失败。您可以通过添加响应验证、检查和阈值(包括响应时间或错误率的阈值),将失败的判定标准添加到测试脚本中。同时,您也可以根据实际情况,灵活地调整这些与性能相关的标准。
根据上述讨论的要点,我对最初的k6基本脚本进行了修改,让它更贴近真实的测试情况。在参考链接--https://gist.github.com/nicolevanderhoeven/fb74cd9769c8abf95eeb8765a49398cb中,也包含了一个带有登录凭据的CSV文件。
执行负载测试
让我们延用上面的k6脚本,并通过执行如下命令,来触发负载测试的运行:
- k6 run test.js
该命令将根据文件中已有的测试参数在本地执行k6脚本。此类小型验证性测试,通常被称为震荡测试(shakeout tests)。当然,我们接下来会在基础架构上运行更加复杂的负载测试。
测试的真实性
当您在数据中心的主机上运行负载测试时,由于测试与应用服务器同属一个网络,其响应时间会比用户真实体验到的要快得多。因此,我们应当设法将负载生成器的位置,与用户的物理位置相匹配。对此,在云端运行测试便是一种增加测试真实性的简便方法,尤其是当您的大多数最终用户是在组织外部的时候。
针对上述例子,您可以通过链接--https://app.k6.io/account/register,注册一个k6的云端帐户(其中,前50个测试是免费的)。由于k6本身是开源的,因此您也可以在自己的云端基础设施上运行它。当然原生的k6云服务会更加直接且便于上手。
在拥有了帐户之后,您可以通过链接--https://app.k6.io/account/api-token,复制API令牌,然后在终端中运行如下命令,以授权本地的k6运行您的帐户:
- k6 login cloud –token
而成功地通过了身份验证之后,您便可以选择在云端运行如下命令了:
- k6 cloud test.js
下图展示了k6执行的负载测试结果。图中的执行模式(execution)则表明您是在云端运行该测试的。
由于您的测试默认会在美国Ashburn区域的k6 AWS账户中运行,因此您可以使用云端执行选项(cloud execution options,请参见--https://k6.io/docs/cloud/creating-and-running-a-test/cloud-tests-from-the-cli#cloud-execution-options),将此设置按虚拟用户的比例,指定到其他区域。
分析负载测试的结果和报告
尽管我们在逻辑上会将分析流程与执行步骤相互分离,但是两者实际上是重叠的。也就是说,上述k6的输出截图中其实已经包含了指向k6云端仪表板的链接,可方便我们在测试运行过程中,实时地查看到测试的结果。这种实时监控的测试方式可以让用户在出现问题时,及时地发现问题,并在必要时中止测试,以对其进行修复。如果测试是由多个负载生成器来执行的,那么k6会有一个统一的仪表板,以展示测试的整体概况。
在k6Cloud中执行负载测试后的结果示例
如果出现大量的错误,或者是响应时间明确地表明某个组件无法处理负载的情况,我们应当立即中止测试,获取必要的信息,以针对出现的瓶颈提出性能改进和解决方案。当然,k6也会帮助用户创建指向仪表板的可共享式链接(请参见--https://app.k6.io/runs/public/f6ad87cec7a24a8c82e46c816e67c4b3),以方便相关团队通过协同“会诊”,发掘出测试数据背后的真实原因。
虽然这些带有测试结果的共享仪表板足以满足敏捷团队,但是对于某些项目而言,则可能需要正式的测试记录总结报告。毕竟,负载测试的目的并不在于测试本身,而是对于结果的处理。我们需要向利益相关者清楚地传达测试结果,以便大家更为有效地解决各种性能问题。
原文标题:An End-to-End Guide to Load Testing,作者: Nicole van der Hoeven
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】