1.简介
如今,无数的框架和库使得在数小时之内从几乎没有内容转换为功能全面的正在运行的应用程序或服务变得非常容易。 确实很棒,您可能会完全避免这样做,但是在特定应用程序的背景下,框架代表您做出的决策(通常称为“敏感默认值”)往往远非最佳(甚至是足够的)或服务(说实话,几乎不可能提出一种适合所有人的解决方案)。
在本教程的这一部分中,我们将讨论性能和负载测试,重点介绍可帮助您实现目标的工具,并重点介绍要调整的应用程序的典型领域。 值得注意的是,某些技术可能适用于单个微服务,但在大多数情况下,重点应放在整个微服务体系结构集合上。
性能和负载测试这两个术语经常可以互换使用,但这是一个误解。 确实,这些测试技术经常结合在一起,但是它们各自设定了不同的目标。 性能测试可帮助您评估被测系统的速度,而负载测试可帮助您了解被测系统的限制。 这些答案对系统正在运行的环境非常敏感,因此始终建议将模拟设计为尽可能接近生产条件。
目录
由于本教程涉及的内容太多,因此本教程的这一部分不包括性能和负载测试的方法。 我强烈推荐Brendan Gregg撰写的《 系统性能:企业和云 》一书,以深刻理解整个软件堆栈中的性能和可伸缩性方面。
2.与JVM和GC成为朋友
Java的成功主要归功于它的运行时( JVM )和自动内存管理( GC )。 多年来, JVM已变成一项非常复杂的技术,并在其之上构建了许多东西。 这就是为什么它通常被称为“ JVM平台”的原因。
目前有两种主要的开放源代码,可用于生产的JVM实现: HotSpot和Eclipse OpenJ9 。 公平地讲, HotSpot处于主导地位,但是Eclipse OpenJ9对于某些类型的应用程序看起来很有希望。 如果不提及基于SubstrateVM的GraalVM (一种高性能的多语言VM),图片将是不完整的。 从一开始,选择正确的JVM可能很容易。
关于内存管理和垃圾回收( GC ),事情要复杂得多。 根据JDK的版本(8或11)和供应商,我们正在谈论串行GC , 并行GC , CMS , G1 , ZGC和Shenandoah 。 JDK 11版本引入了实验性Epsilon GC ,它实际上是无操作的GC 。
调优GC是一门艺术 ,需要深入了解JVM的工作原理 。 JVM解剖园是关于JVM和GC内部的宝贵见解的最佳且最新的来源之一。 但是,您将如何诊断应用程序中的问题并真正找出要调整的内容呢?
幸运的是,现在有了两个出色的工具Java任务控制和Java Flight Recorder的帮助 ,它们已经从JDK 11版本开始开源。 这些工具仅适用于HotSpot VM,即使在生产中也非常易于使用 。
最后但并非最不重要的一点,让我们讨论一下容器化(或者更好的说, Docker '化)如何影响JVM行为。 自从JDK 10和JDK 8 Update 191起 ,JVM已经进行了修改,以完全意识到它正在Docker容器中运行,并且能够正确提取分配的CPU和总内存。
3.微基准
很难根据应用程序和服务的需要调整GC和JVM设置,但这是有益的。 但是,当JVM偶然发现效率低下的代码时,很可能将无济于事。 通常,必须从头开始重写或重构实现,但是如何确保其性能优于旧的呢? JHM工具支持的微基准测试技术可以为您提供帮助。
JMH 是用于构建,运行和分析以Java和其他针对JVM的其他语言编写的nano / micro / milli / macro基准测试的Java工具。 – https://openjdk.java.net/projects/code-tools/jmh/
您可能想知道为什么要为此使用专用工具? 简而言之,基准测试看起来很容易,只需循环运行有问题的代码并测量时间,对吗? 实际上,编写基准来正确衡量应用程序中相当小的部分的性能非常困难,尤其是在涉及JVM的情况下。 考虑到基准测试的隔离代码片段的范围很小,JVM可以应用许多优化。 这是您需要使用诸如JHM之类的工具的主要原因,该工具能够了解JVM的行为并指导您正确实施和运行基准测试,因此您将获得可以信任的度量。
JHM存储库中有大量示例可供您查看和获取,但是如果您想了解更多有关该主题的信息, Benjamin J Evans , James Gough和Chris Newland撰写的“ 优化Java:提高JVM应用程序性能的实用技术”是一个很好的例子。很棒的书。
一旦掌握了JHM并开始每天使用它,比较微基准可能会变得很繁琐。 JMH Compare GUI是一个小型GUI工具,可以帮助您直观地比较这些结果。
4. Apache JMeter
让我们从微基准测试转移到宏观基准测试,并谈谈测量部署在某处的应用程序和服务的性能。 我们要研究的第一个工具是Apache JMeter ,它可能是该类别中最古老的工具之一。
所述 的Apache JMeter的 应用是开源软件,一个100%纯Java应用程序设计成负载测试功能行为和测量性能。 它最初是为测试Web应用程序而设计的,但此后已扩展到其他测试功能。 – https://jmeter.apache.org/
Apache JMeter提倡基于UI的方法来创建和管理相当复杂的测试计划。 UI本身非常直观,无需花费很长时间即可提出您的第一个方案。 Apache JMeter最强大的方面之一是高水平的可扩展性和脚本支持。
Reservation Service是JCG Car Rentals平台的核心,因此下面的屏幕快照简要介绍了针对保留RESTful API的简单测试计划。
用户友好的界面的存在对人类而言非常重要,但对于自动化工具而言却不是。 幸运的是, Apache JMeter测试计划可以使用Apache Maven插件 , Gradle插件 从命令行运行,甚至可以嵌入到应用程序测试工具中 。
易于注入到持续集成管道中的能力使Apache JMeter非常适合开发自动化负载和性能测试方案。
5.加特林
有很多负载测试框架促进了代码优先的方法来测试场景,而加特林(Gatling)就是最好的例子之一。
Gatling 是一种功能强大的负载测试工具。 其设计目的是易于使用,可维护性和高性能。 – https://gatling.io/docs/current/
Gatling场景是用Scala编写的,但是这一方面在简洁的DSL之后被抽象化了,因此尽管不是必需的,但还是需要Scala的知识。 让我们使用加特林代码优先方法为保留服务重新实现Apache JMeter测试方案。
class ReservationSimulation extends Simulation {
val tokens: Map[String, String] = TrieMap[String, String]()
val customers = csv("customers.csv").circular()
val protocol = http
.baseUrl("http://localhost:17000")
.contentTypeHeader("application/json")
val reservation = scenario("Simulate Reservation")
.feed(customers)
.doIfOrElse(session => tokens.get(session("username").as[String]) == None) {
KeycloakToken
.token
.exec(session => {
tokens.replace(session("username").as[String], session("token").as[String])
session
})
} {
exec(session => {
tokens.get(session("username").as[String]).fold(session)(session.set("token", _))
})
}
.exec(
http("Reservation Request")
.post("/reservations")
.header("Authorization", "Bearer ${token}")
.body(ElFileBody("reservation-payload.json")).asJson
.check(status.is(201)))
setUp(
reservation.inject(rampUsers(10) during (20 seconds))
).protocols(protocol)
}
测试场景或就Gatling而言 ,很容易遵循。 由于需要使用Keycloak API获取访问令牌, 因此会产生一些小麻烦,但是有几种解决方法。 在上面的模拟中,我们使其成为了内存中令牌缓存支持的保留流程的一部分。 如您所见,在Gatling中 ,复杂的多步仿真看起来很容易。
加特林的报告方面真的很棒。 开箱即用,您可以通过漂亮的HTML标记获得仿真结果,下面的图片只是其中的一小部分。 您也可以从模拟日志文件中提取此数据,并按需要的方式对其进行解释。
从早期开始, Gatling就专为进行连续负载测试而设计,并且与Apache Maven , SBT , Gradle和持续集成管道很好地集成在一起 。 有许多扩展可用于支持多种协议(当然,欢迎您在其中做出贡献)。
6.命令行工具
命令行工具可能是最快的方法,最直接的方法是在您的服务上增加一些负担,并Swift获得所需的反馈。 我们将从Apache Bench (更好地称为ab )开始,该工具用于对基于HTTP的服务和应用程序进行基准测试。
例如,假设先前已获得安全性令牌,则可以使用ab对我们在上一节中看到的预订服务的相同场景进行负载测试。
$ ab -c 5 -n 1000 -H "Authorization: Bearer $TOKEN" -T "application/json" -p reservation-payload.json http://localhost:17000/reservations
This is ApacheBench, Version 2.3
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
...
Completed 1000 requests
Finished 1000 requests
Server Software:
Server Hostname: localhost
Server Port: 17000
Document Path: /reservations
Document Length: 0 bytes
Concurrency Level: 5
Time taken for tests: 22.785 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 487000 bytes
Total body sent: 1836000
HTML transferred: 0 bytes
Requests per second: 43.89 [#/sec] (mean)
Time per request: 113.925 [ms] (mean)
Time per request: 22.785 [ms] (mean, across all concurrent requests)
Transfer rate: 20.87 [Kbytes/sec] received
78.69 kb/s sent
99.56 kb/s total
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 1
Processing: 6 113 449.4 27 4647
Waiting: 5 107 447.7 19 4645
Total: 6 114 449.4 28 4648
Percentage of the requests served within a certain time (ms)
50% 28
66% 52
75% 57
80% 62
90% 83
95% 326
98% 1024
99% 2885
100% 4648 (longest request)
当ab的简单性成为展示的标杆时,您可能会看到wrk (一种现代的HTTP基准测试工具)。 它具有由Lua烘焙的强大脚本支持,并且能够模拟复杂的负载情况。
$ wrk -s reservation.lua -d60s -c50 -t5 --latency -H "Authorization: Bearer $TOKEN" http://localhost:17000/reservations
Running 1m test @ http://localhost:17000/reservations
5 threads and 50 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 651.87ms 93.89ms 1.73s 85.20%
Req/Sec 16.94 10.00 60.00 71.02%
Latency Distribution
50% 627.14ms
75% 696.23ms
90% 740.52ms
99% 1.02s
4579 requests in 1.00m, 2.04MB read
Requests/sec: 76.21
Transfer/sec: 34.83KB
如果您不想使用脚本,那么当然值得一提的还有一个很棒的选择, vegeta ,一个HTTP负载测试工具(和库)。 它具有大量功能,甚至包括开箱即用的绘图功能。
$ echo "POST http://localhost:17000/reservations" | vegeta attack -duration=60s -rate=20 -header="Authorization: Bearer $TOKEN" -header="Content-Type: application/json" -body reservation-payload.json > results.bin
一旦存储了相应的负载测试结果(在我们的示例中,在名为results.bin的文件中),就可以轻松地将其转换为文本报告:
$ cat results.bin | vegeta report
Requests [total, rate] 1200, 20.01
Duration [total, attack, wait] 59.9714976s, 59.9617223s, 9.7753ms
Latencies [mean, 50, 95, 99, max] 26.286524ms, 9.424435ms, 104.754362ms, 416.680833ms, 846.8242ms
Bytes In [total, mean] 0, 0.00
Bytes Out [total, mean] 174000, 145.00
Success [ratio] 100.00%
Status Codes [code:count] 201:1200
Error Set:
或者只是转换成图形化的图表表示形式:
$ cat results.bin | vegeta plot
正如我们已经看到的那样,这些命令行工具中的每一个都可以满足您在特定负载或性能情况下可能想到的不同需求。 尽管还有很多其他选择,但是这三个是非常安全的选择。
TCP?
到目前为止,我们讨论的所有工具从一开始就支持对基于HTTP的Web服务和API的性能测试。 但是,如何强调依赖于gRPC , HTTP / 2甚至普通的旧UDP协议的服务呢?
尽管还没有魔术般的瑞士军刀工具,但肯定有一些选择。 例如,自3.0.0
版本以来, 加特林内置HTTP / 2支持 ,而社区扩展支持gRPC和UDP 。 另一方面, vegeta具有HTTP / 2支持,而Apache JMeter具有SMTP , FTP和TCP支持。
详细介绍了官方的gRPC基准测试指南 ,该指南概述了性能基准测试工具,测试考虑的场景以及基于gRPC的服务的测试基础结构 。
8.我们周围的更多工具
除了我们到目前为止讨论的工具和框架之外,值得一提的是其他一些不错的选择,但对于Java开发人员而言可能不是本地选择。 第一个是Locust ,这是一个使用Python编写的易于使用,分布式,可伸缩的负载测试框架。 第二个是Tsung ,这是一个用Erlang编写的开源多协议分布式负载测试工具。
值得关注的有希望的项目之一是Test Armada ,这是一组使开发人员能够大规模实施质量自动化的工具,该工具也有望引入性能测试的支持(基于Apache JMeter )。
而且,如果不谈论Grinder ,这是不公平的,因为Grinder是最早的Java负载测试框架之一,可以轻松地使用许多负载注入器机器运行分布式测试。 不幸的是,该项目似乎已经死了,最近几年没有任何发展迹象。
9.针对Java开发人员的微服务:性能和负载测试–结论
在本教程的这一部分中,我们讨论了用于性能和负载测试的工具,技术和框架,特别是在JVM平台的上下文中。 花足够的时间,预先设定目标并设计实际的性能和负载测试方案非常重要。 这本身就是一门学科,但是最重要的是,这些模拟的结果可以指导服务所有者正确塑造我们之前讨论的许多SLA方面 。
10.下一步是什么
在本教程的下一部分中,我们将通过围绕安全性测试的工具来总结与测试微服务有关的讨论。
此部分的示例和源可在此处下载。