InfoQ主页文章使用Gatling加载测试API和网站:开始永远不会太晚。
针对API和网站进行负载测试,既可以验证经过长时间开发的性能,又可以从应用中获得有用的反馈,从而提高应用的伸缩能力和性能。
工程师应该避免创建负载测试的“大教堂”,最终几乎没有时间来提高整体性能。编写尽可能简单的测试并从那里迭代。
加特林可用于进行压力测试、浸泡测试和容量测试。还可以编写“当时给予”风格的测试。
确定任何负载测试的目标、约束和条件都很重要。始终识别并验证任何假设,例如用户的默认设备、网络速度、应用程序将在生产上运行的服务器类型。
你打开你的软件工程日志,开始写--“研发团队的日志。我们从来没有预见到我们的应用程序会出现这样的失败,坦率地说,我不明白。我们拥有一切,这是一件杰作!测试是绿色的,指标是好的,用户是满意的,但当流量成群结队地到来时,我们仍然失败了。“。
停下来深吸一口气,你继续说-“我们认为我们已经准备好了;我们认为我们已经掌握了运营一个受欢迎的网站的诀窍。我们做得怎么样?一点都不接近。我们最初认为TechCrunch上的链接不会带来那么大的流量。我们还认为我们可以应对电视广告播出后的载客量激增。如果应用程序在启动后立即停止响应,那么所有这些测试的意义是什么,而我们无法解决这个问题,不管我们向它扔了多少服务器,试图尽我们所能挽救这种情况。“。
停顿了很久之后,“已经很晚了,时间在流逝。还有太多事情要告诉你,太多我希望我知道的事情,太多我希望我从未犯过的错误。“。不可阻挡,你写道,“如果我们能做点什么让它工作就好了……”
然后,清晰地回击您-您会想:我们为什么要首先进行负载测试?是要在经过长时间的开发后验证性能,还是真的要从我们的应用程序中获得有用的反馈,并提高其伸缩能力和性能?在这两种情况下都会进行验证,但在后一种情况下,获取反馈是过程的中心。
您看,这实际上并不是关于负载测试本身,目标是确保您的应用程序在上线时不会崩溃。您不会想要编写负载测试的“教堂”,最终几乎没有时间来提高整体性能。通常,即使不是全部时间,也是你想要花费最多时间的地方,那就是致力于改进。负载测试只是达到目的的一种手段,没有别的。
如果你害怕是因为你的截止日期是明天,而你正在寻找一个快速的胜利,那么欢迎,这是给你的文章。
在这篇文章中,我们将编写一些代码,更准确地说,是Scala代码,因为我们将使用Gatling。虽然代码看起来可能很可怕,但我可以保证它不是。事实上,Scala可以暂时搁置一边,我们可以将Gatling看作它自己的语言:
导入io.gatling.core.Predef._ 导入io.gatling.http.Predef._ 类SimplestPossibleSimulation扩展了Simulation{ VAL baseHttpProtocol= Http.baseUrl(";https://computer-database.gatling.io";) VAL SCN=方案(";最简单";) .exec( HTTP(";主页";) .Get(";/";) ) 设置( Scn.inject(atOnceUsers(1)) ).协议(BaseHttpProtocol) }。
让我们把它分成更小的部分。由于Gatling是使用Scala构建的,而Scala本身运行在Java虚拟机上,因此有一些相似之处。第一个问题是代码总是以包导入开始:
它们包含所有的Gatling语言定义,您将根据需要随时使用这些定义以及其他导入。然后,创建足够的内容来封装模拟:
SimplestPossibleSimulation是模拟的名称,而SimplestPossibleSimulation是由Gatling定义的一个结构,您可以对其进行“扩展”,并将包含所有模拟代码,分为三个部分:(1)我们需要定义我们想要使用的协议和我们希望它具有的参数:(1)我们需要定义我们想要使用的协议和我们想要它拥有的参数:
请注意,我使用了术语“场景”,尽管到目前为止我们只讨论了模拟。虽然它们经常被混为一谈,但它们实际上都有不同的含义:
一个完整的场景描述了单个用户在应用程序上执行的旅程,根据应用程序的类型,从一个页面导航到另一个页面,从一个端点导航到另一个端点,依此类推。
模拟测试是对分配给其方案的人群(例如:管理员和用户)进行的完整测试的定义。
因为模拟大致是场景的集合,所以它是配置了自己的注入配置文件的所有场景的总和(我们现在将忽略这一点)。方案中完成的所有请求所基于的协议既可以共享(如果在设置后链接),也可以根据方案定义(如果我们有多个方案,scn1和scn2),如下所示:
注意所有内容是如何链接在一起的,用逗号分隔:scn1配置了这样的注入配置文件和这样的协议,等等。
当您运行Gatling模拟时,它将同时启动内部配置的所有场景,这就是我们现在要做的。
运行Gatling模拟的一种方式是使用Gatling开源捆绑包。它是一个自包含的项目,其中包含一个用于放置模拟文件的文件夹和一个用于运行这些文件的脚本。
所有安装都需要正确的Java安装,至少JDK 8,有关所有详细信息,请参阅《Gatling Installation Docs》(Java Gatling安装文档)。
解压缩包后,您可以在user-files/Simulation文件夹中创建一个名为SimplestPossibleSimulation.scala的文件。然后,在命令行中,在捆绑包文件夹的根目录下键入:
这将扫描前一个文件夹中的模拟并提示您,询问您要运行哪一个。由于捆绑包包含一些示例,因此您将有多个可供选择。
虽然这是运行Gatling模拟的最简单方式,但它不能很好地与源代码库配合使用。首选的方法是使用构建工具,如Maven或TSBT。如果这是您想要尝试的解决方案,您可以使用GIT克隆我们的演示存储库:
或者,如果您想继续阅读本文,您可以克隆为此目的而创建的以下存储库:
运行测试后,您注意到在输出的末尾有一个URL,您打开并偶然发现了该URL:
您会注意到有一个名为“Home Redirect1”的请求不是我们自己提出的。您可能会认为这可能是Gatling自动跟随重定向,并将所有不同查询的响应时间分开。但它看起来确实平淡无奇,你可能是对的。
这对于调试目的来说很棒,但是对于负载测试来说并不是很令人兴奋。问题是,编写注入配置文件没有正确或错误的方法,但首先要做的是事情。
注入配置文件是一系列规则,按提供的顺序执行,描述您希望用户启动自己的方案的速率。虽然您可能不知道必须编写的确切配置文件,但您几乎总是对预期行为有所了解。这是因为您通常要么预期在特定时间点会有大量的传入用户,要么希望随着业务的增长会有更多的用户到来。
需要考虑的问题包括:您希望用户同时到达吗?如果你打算提供闪电促销,或者你的网站很快就会出现在电视上,情况就是这样;你对你的用户将遵循的模式有什么想法吗?它可能是用户在特定的时间到达,分布在一天中,或者只在工作时间内出现,等等。这些知识将给你一个攻击的角度,我们称之为一种测试。我将介绍其中的三个。
当我们想到负载测试时,通常会想到“压力测试”,结果发现这只是一种类型的测试;“闪电销售”是其基本含义。
这个想法很简单:有很多用户,尽可能少的时间。TatOnceUsers非常适合这一点,但有一些警告:
很难说在不对硬件要求过高的情况下,可以同时启动多少用户。有很多可能的限制:CPU、内存、带宽、Linux内核可以打开的连接数、机器上可用套接字的数量,等等。过高的值很容易使硬件紧张,结果是毫无意义的结果吗?
这是您可能需要多台机器来运行测试的地方。举个例子,Linux内核,如果优化得当,每秒可以很容易地打开5k连接,10k已经太多了。
在一分钟内拆分10,000个用户仍然被认为是压力测试,因为时间限制非常繁重:
如果时间跨度变得太长,用户的旅程会开始感觉更熟悉。考虑“每天的用户”、“每周的用户”等等。然而,模拟用户是如何一天到达的不是浸泡测试的目的,而是结果。
在浸泡测试时,您要测试的是很长一段时间下的系统行为:CPU的行为如何?我们能看到任何内存泄漏吗?这些磁盘的行为如何?电视网?
要做到这一点,一种方法是对经过很长一段时间到达的用户进行建模:
这感觉就像是在做“每天的用户”。不过,如果您的目标是对您的分析进行建模,那么计算您的提升正在进行的每秒用户数并缩短持续时间会让您更快地获得结果:
谈到这一点,rampUsers对constantUserPerSec的关注只是口味问题。前者为您提供了到达的总用户的简单概述,而后者则更多地是关于吞吐量。然而,更多地考虑吞吐量可以更容易地进行“提升”,即逐步到达最终目的地:
最后,您可以简单地测试您的系统可以处理多少吞吐量。在这种情况下,容量测试是可行的。结合前面测试中的方法,我们的想法是从某个任意时间调整吞吐量,增加负载,再次调整,然后继续,直到一切都下降,我们得到一个限制。类似于:
Scn.inject( ConstantUsersPerSec(10)在5分钟内, 在30秒内,rampUsersPerSec(10)到20, ConstantUsersPerSec(20)在5分钟内, 在30秒内,rampUsersPerSec(20)到30, ..。 )。
您可能会说,这样做20次可能有点麻烦,…。但是,由于Gatling的基础是代码,您可以生成一个循环来生成先前的注入配置文件,或者使用我们专门用于容量测试的DSL:
如果这是您第一次进行负载测试,无论您是否已经知道目标用户行为,您都应该从容量测试开始。压力测试是有用的,但在这样的负载下分析指标确实很棘手。由于所有的事情都同时失败,这使得这项任务变得困难,甚至是不可能的。容量测试提供了慢慢走向失败的奢侈品,这对于第一次分析来说更舒服。
要开始,只需运行一个容量测试,使您的应用程序尽快崩溃。当一切看起来都很顺利时,您只需要增加场景的复杂性。
上图显示了响应时间百分比。在负载测试时,我们可能会倾向于使用平均值来分析全局响应时间,这很容易出错。如果平均数能让你对跑步过程中发生的事情有一个快速的概述,那么它就会把你真正想看的所有东西都藏在地毯下面。这就是百分位数派上用场的地方。
这样想一想:如果平均响应时间是几毫秒,那么对于1%的用户群来说,在最糟糕的情况下体验会是什么感觉呢?是更好还是更糟?您0.1%的用户对此有何感想?以此类推,越来越接近零。用户和请求的数量越多,为了研究极端行为,您就需要更接近于零。举个例子,如果您有300万个用户在您的应用程序上执行单个请求,其中0.01%的用户超时,那么将有30000个用户无法访问您的应用程序。
通常使用百分位数,这与反过来思考这个问题相对应。对于用户来说最差的1%的情况变成了“99%的用户最好的体验如何”,0.1%变成了99.9%,依此类推。建模计算的一种方法是按升序对所有响应时间进行排序并标记点:
从这里开始,您将达到99%,并根据您有多少用户,通过添加9来尽可能接近100%。
在前面的图表中,我们有一个63毫秒的99百分位数,但是这是好还是不好呢?最高为65人,看起来是这样的。不过,如果是10毫秒会不会更好呢?
大多数情况下,指标是上下文相关的,本身没有任何意义。一般说来,本地主机上10ms的响应时间不是一个成就,而且由于光速的限制,从巴黎到澳大利亚也是不可能的。我们不得不问,“实际运行是在什么条件下进行的?”这将帮助我们很大程度上推断这次跑步是否真的那么好。这些条件包括:
这是最重要的部分。如果你知道一个测试的运行条件,你可以创造奇迹。您可以(也应该)在本地测试,所有东西都在您的计算机上运行,只要您了解它归根结底是什么:不能推断它将如何在生产中运行,但这允许您进行回归测试。只需比较多个运行之间的指标,它将告诉您性能是更好还是更差。
要做到这一点,您不需要完整的测试环境。如果您知道您的最终用户是移动用户,使用位于单个数据中心的所有计算机进行测试可能会导致灾难。然而,你也不需要百分之百的现实。只需记住它意味着什么,以及您可以从测试条件中推断出什么:数据中心是一个巨大的本地网络,结果不能与移动网络相提并论,而观察到的结果实际上会比现实好得多。
您不仅应该了解测试运行的条件,还应该事先决定测试是成功还是失败。为此,我们定义标准,如下所示:
不过,这其中有一个问题。在这三个验收标准中,实际上只有一个标准可以用来描述负载下的系统,您能猜到可以用哪些标准和什么标准来替换它们吗?
(1)“250ms以下的平均响应时间”:从上一节自然可以看出,虽然平均不算差,但不足以描述用户行为,应该始终以百分位数为辅:
(2)“2000活跃用户”:这个比较棘手。如今,我们充斥着显示“活跃用户”之类的分析,因此使用这种度量来定义验收标准应该是很有诱惑力的。不过,这是一个问题。直接对一定数量的活跃用户建模的唯一方法是创建封闭模型。在这里,您最终将得到一个用户队列,这就是问题所在。你好好想想看。如果您的应用程序速度变慢,并且用户在队列中,他们将开始在队列中堆积,但只有一小部分(应用程序中允许的最大数量)将处于“活动”状态并执行请求。活跃用户的数量将保持不变,但用户将像在商店一样在外面等待。
在现实生活中,如果一个网站出现故障,人们会继续刷新页面,直到出现问题,这将进一步恶化网站的性能,直到人们放弃,而你失去了他们。除非是您的用例,否则您不应该为活跃用户建模-相反,您应该以一定数量的活跃用户为目标。这可能很困难,但却是可行的。这将取决于:场景的持续时间,包括响应时间、暂停等、网络、注入配置文件等。
(3)事实上,“低于1%的失败请求”是正确表示介于三者之间的系统负载的唯一标准。然而,这不能作为经验法则。根据使用情形的不同,1%可能太高。想一想一个电子商务网站,你可能会允许一些页面在这里和那里出现故障,但在购买之前的转换漏斗末尾出现故障将会对您的业务模式造成致命的影响。您可以将此特定请求的失败标准设为0%,其余的则设置为更高的百分比,或者根本不设置任何标准。
所有这些都导致了一个基于给定的何时-然后模型的规范,以及如何从总体上考虑负载测试,以及我们到目前为止所学到的一切:
导入io.gatling.core.Predef._ 导入io.gatling.http.Predef._ 导入scala.concurrent.Duration。_ 类FullySpecifiedSimulation扩展了Simulation{ VAL baseHttpProtocol= Http.baseUrl(";https://computer-database.gatling.io";) //何时 VAL SCN=方案(";最简单";) .exec( HTTP(";主页";) .Get(";/";) ) //给定 设置( Scn.inject( 在1分钟内,rampUsersPerSec(1)增加到200, 9分钟内的constantUsersPerSec(200) ) ).协议(BaseHttpProtocol)//然后 .assertions( Global.requestsPerSec.gte(100), Global.responseTime.Percententile(99).lt(250), Global.ailedRequests.percent.lte(1) ) }
场景(何时)部分没有更改(到目前为止),但是您将需要(给定的)同一位置的注入配置文件,并且需要一个称为断言的新部分。断言所做的是从所有模拟中计算度量,如果它们低于/超过需求,则“构建”将失败。例如,使用Maven项目,您将看到:
[信息]- [信息]构建成功 [信息]。 [信息]总时间:07:07分钟 [信息]完成时间:2020-08-27T20:52:52+02:00 [信息]-。
否则构建失败。当使用像Jenkins这样的CI工具时,这是很方便的,我们为它提供了一个新的Gatling Jenkins插件。使用它,您可以将模拟的运行直接配置到您的配置项中,如果您的验收标准不合格以及失败的程度,您可以得到通知。
请注意,在前面的示例中,我们使用关键字“global”作为起点,但您可以像单个请求没有失败一样精确,而忽略所有其他请求:
如果您认为某个特定变量(例如网络)是导致性能低下的罪魁祸首,那么您将需要一个适当的测试协议来测试您的假设。每个变量都应该针对“证人”进行测试。如果你怀疑“某样东西”是原因,在有或没有的情况下进行测试,然后进行比较。制定一个基线,做一些小的变化,然后对照这个进行测试。
额外的工具将帮助捕捉Gatling不能提供的基本信息。因为Gatling给出的所有指标都是从用户的角度出发的,所以您需要确保您在频谱的另一端配备了系统和应用程序监控。在加特林一侧安装系统监控也非常有用。它将帮助您查看在包含CPU、内存、网络连接及其状态、网络问题等信息的大型测试中是否使用了过多的机器资源。
有许多流行的指标收集解决方案,例如Prometheus与Node Exporter相结合。这些可以与Grafana和适当的仪表盘结合使用。让它一直运行并收集指标。在时间窗口上运行测试时检查。
.