《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Performance-Testing/SpringMVC-Webflux-benchmark/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1. 概述

《性能测试 —— SpringMVC、Webflux 基准测试》 中,我们已经看了 SpringMVC 和 Webflux 的性能测试。而本文,我们来看看 Java 技术栈里,主流的两个网关的选择:

  • Zuul :由 Netflix 开源的,基于阻塞 IO 的 API 网关。

    本文,测试的 Zuul 是 1.x 版本。

    虽然 Netflix 后来发布了 Zuul 2.x 版本,基于非阻塞 IO 的 API 网关实现,但是从 how to use zuul2 like the spring-cloud-starter-zuul? 上的讨论来看,Spring Cloud 官方,并不考虑退出 Spring Cloud Zuul 2.x 的支持。当然,原因也非常简单,相信胖友懂的。

  • Spring Cloud Gateway :由 Spring Cloud 源码的,基于 Webflux 实现的非阻塞 IO 的 API 网关。

2. 性能指标

《性能测试 —— Nginx 基准测试》 保持一致,我们还是以 QPS 作为性能的指标。

https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-07 中,我们提供了两个示例,分别是:

  • lab-07-spring-cloud-gateway :Spring Cloud Gateway 2.1.0.RELEASE

    底层基于 Webflux + Netty 运行环境

  • lab-07-zuul :Zuul 1.3.1 + Tomcat

    底层基于 Servlet 实现,感兴趣的可以看看 ZuulServlet 。

胖友可以使用 mvn package 命令,打包出不同的示例,进行压力测试。

当然,既然是网关,我们肯定还是要提供一个后端 API 服务,那么我们使用在 《性能测试 —— Tomcat、Jetty、Undertow 基准测试》lab-05/lab-05-tomcat 这个示例。这样,就是网关到这个后端 API 服务。

为了让这个测试更加有趣,我们也会多测试一个 Nginx 转发请求到后端 API 服务,因为在大多数公司未引入网关的情况下,都是 Nginx 转发请求到后端服务。

本文的 Nginx 做了些配置调整,以达到较好的性能状态。胖友可以参考如下两篇文章,进行自己玩耍下:

特别需要注意,worker_connections 一定要调大到 65535 ,因为默认是 1000 连接时,在进行 HTTP 压测时,性能会急剧下降,因为每个 Worker 的连接数太小。

所以,本文的整个测试一共有三种:

3. 测试环境

  • 型号 :ecs.c5.xlarge

    艿艿:和我一样抠门(穷)的胖友,可以买竞价类型服务器,使用完后,做成镜像。等下次需要使用的时候,恢复一下。HOHO 。

  • 系统 :CentOS 7.6 64位

  • CPU :4 核

  • 内存 :8 GB

  • 磁盘 :40 GB ESSD 云盘

  • JDK :openjdk version "1.8.0_212"

  • JVM 参数 :-Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k

因为我们在跑的过程,发现 wrk 占用 CPU 不是很高,所以直接本机运行。

有一点要注意,JVM 本身有预热的过程,Spring Cloud Gateaway、Zuul 本也有预热的过程(例如说,线程的初始化),所以需要多次测试,取平均值。

本文,我们使用 wrk 如下命令进行测试:

./wrk -t50 -c并发 -d30s http://127.0.0.1:8080

  • -t50 参数,设置 50 并发线程。
  • -c并发 参数,设置并发连接,目前会按照 300、1000、3000、5000 的维度,进行测试。
  • -d30s 参数,设置执行 30s 的时长的 HTTP 请求。
  • http://127.0.0.1:8080 参数,请求本地的 Web 服务。

4. 第一轮

4.1 API 服务

在正式测试网关性能之前,我们先来测试下,API 服务在不同并发下,性能的情况。

启动 API 服务,命令如下:

java -jar lab-05-tomcat-1.0-SNAPSHOT.jar -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k

🚚 300 并发

$wrk -t50 -c300 -d30s http://127.0.0.1:8080/hello

Running 30s test @ http://127.0.0.1:8080/hello
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 10.52ms 8.48ms 198.51ms 93.18%
Req/Sec 604.85 74.09 3.05k 80.54%
905070 requests in 30.10s, 102.01MB read
Requests/sec: 30066.98
Transfer/sec: 3.39MB

  • QPS 为 30066.98 。
  • 平均延迟为 10.52ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s http://127.0.0.1:8080/hello

Running 30s test @ http://127.0.0.1:8080/hello
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 33.76ms 6.79ms 126.37ms 79.91%
Req/Sec 594.53 59.55 1.66k 77.89%
891385 requests in 30.10s, 100.46MB read
Requests/sec: 29614.75
Transfer/sec: 3.34MB

  • QPS 为 29614.75 。
  • 平均延迟为 33.76ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s http://127.0.0.1:8080/hello

Running 30s test @ http://127.0.0.1:8080/hello
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 99.94ms 15.34ms 438.85ms 91.86%
Req/Sec 601.61 94.23 4.53k 88.85%
903962 requests in 30.10s, 101.87MB read
Requests/sec: 30032.17
Transfer/sec: 3.38MB

  • QPS 为 30032.17 。
  • 平均延迟为 99.94ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s http://127.0.0.1:8080/hello

50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 167.14ms 25.40ms 555.49ms 94.09%
Req/Sec 597.09 174.25 6.35k 78.79%
895521 requests in 30.10s, 100.87MB read
Requests/sec: 29751.10
Transfer/sec: 3.35MB

  • QPS 为 29751.10 。
  • 平均延迟为 167.14ms 。

小结

总的来说,QPS 在 30000 左右。

4.2 Spring Cloud Gateway

启动 Spring Cloud Gateway 服务,命令如下:

java -jar lab-07-spring-cloud-gateway-1.0.0.jar -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k

🚚 300 并发

$wrk -t50 -c300 -d30s http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 35.06ms 18.71ms 205.46ms 70.11%
Req/Sec 175.00 29.25 455.00 70.58%
262652 requests in 30.10s, 30.06MB read
Requests/sec: 8725.78
Transfer/sec: 1.00MB

  • QPS 为 8725.78 。
  • 平均延迟为 35.06ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 114.91ms 70.94ms 1.15s 75.94%
Req/Sec 180.65 44.88 444.00 71.41%
270352 requests in 30.10s, 30.94MB read
Requests/sec: 8981.58
Transfer/sec: 1.03MB

  • QPS 为 8981.58 。
  • 平均延迟为 114.91ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 3000 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 314.83ms 211.95ms 1.96s 75.12%
Req/Sec 194.02 93.66 0.96k 72.62%
263753 requests in 30.11s, 30.18MB read
Requests/sec: 8760.64
Transfer/sec: 1.00MB

  • QPS 为 8760.64 。
  • 平均延迟为 314.83ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 579.20ms 386.71ms 3.23s 73.00%
Req/Sec 183.08 102.92 0.94k 68.62%
237143 requests in 30.16s, 27.14MB read
Requests/sec: 7862.68
Transfer/sec: 0.90MB

  • QPS 为 7862.68 。
  • 平均延迟为 579.20ms 。
  • 为了避免 wrk 测试 HTTP 请求超时,我们增加了 -T30 参数,即 30 秒超时时间。

4.3 Zuul

启动 Zuul 服务,命令如下:

java -jar lab-07-zuul-1.0.0.jar -Xms2g -Xmx2g -Xmn1g -XX:MaxMetaspaceSize=256m -Xss256k

🚚 300 并发

$wrk -t50 -c300 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 52.74ms 63.66ms 891.03ms 87.37%
Req/Sec 169.16 51.88 540.00 69.59%
253446 requests in 30.10s, 33.16MB read
Requests/sec: 8420.10
Transfer/sec: 1.10MB

  • QPS 为 8420.10 。
  • 平均延迟为 52.74ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 123.71ms 57.39ms 940.58ms 88.47%
Req/Sec 167.60 26.11 820.00 78.58%
250403 requests in 30.10s, 32.75MB read
Requests/sec: 8319.87
Transfer/sec: 1.09MB

  • QPS 为 8319.87 。
  • 平均延迟为 123.71ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 352.07ms 59.96ms 1.17s 87.87%
Req/Sec 169.50 49.45 1.53k 77.05%
254798 requests in 30.10s, 33.29MB read
Requests/sec: 8465.08
Transfer/sec: 1.11MB

  • QPS 为 8420.10 。
  • 平均延迟为 52.74ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 578.86ms 89.32ms 1.41s 87.56%
Req/Sec 168.64 67.56 1.61k 79.38%
253573 requests in 30.10s, 33.13MB read
Requests/sec: 8425.73
Transfer/sec: 1.10MB

  • QPS 为 8425.73 。
  • 平均延迟为 578.86ms 。

4.4 Nginx

配置 Nginx ,转发到 API 服务。

🚚 300 并发

$wrk -t50 -c300 -d30s -T30 http://127.0.0.1/hello

Running 30s test @ http://127.0.0.1/hello
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 186.59ms 325.52ms 3.03s 82.92%
Req/Sec 359.63 172.84 1.12k 65.58%
515757 requests in 30.10s, 80.15MB read
Requests/sec: 17135.13
Transfer/sec: 2.66MB

  • QPS 为 17135.13 。
  • 平均延迟为 186.59ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s -T30 http://127.0.0.1/hello

Running 30s test @ http://127.0.0.1/hello
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 345.49ms 624.30ms 7.03s 91.87%
Req/Sec 359.08 227.23 1.97k 71.05%
502186 requests in 30.10s, 78.04MB read
Socket errors: connect 0, read 16, write 0, timeout 0
Requests/sec: 16684.66
Transfer/sec: 2.59MB

  • QPS 为 16684.66 。
  • 平均延迟为 345.49ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s -T30 http://127.0.0.1/hello

Running 30s test @ http://127.0.0.1/hello
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 521.34ms 1.01s 15.09s 91.57%
Req/Sec 342.70 244.99 3.73k 78.23%
492682 requests in 30.10s, 76.57MB read
Requests/sec: 16370.08
Transfer/sec: 2.54MB

  • QPS 为 16370.08 。
  • 平均延迟为 521.34ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1/hello

Running 30s test @ http://127.0.0.1/hello
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 769.14ms 1.46s 15.10s 90.37%
Req/Sec 341.61 284.07 6.46k 83.71%
493152 requests in 30.10s, 76.65MB read
Requests/sec: 16382.78
Transfer/sec: 2.55MB

  • QPS 为 16382.78 。
  • 平均延迟为 769.14ms 。

4.5 小结

  • 300 并发时:Nginx > Spring Cloud Gateway > Zuul (17135.13 > 8725.78 > 8420.10)

    Nginx 性能大大领先,Spring Cloud Gateway 略微优于 Zuul

  • 1000 并发时:Nginx > Spring Cloud Gateway > Zuul (16684.66 > 8981.58 > 8319.87)

    Nginx 性能大大领先,Spring Cloud Gateway 略微优于 Zuul

  • 3000 并发时:Nginx > Spring Cloud Gateway > Zuul (16370.08 > 8760.64 > 8420.10)

    Nginx 性能大大领先,Spring Cloud Gateway 略微优于 Zuul

  • 5000 并发时:Nginx > Zuul > Spring Cloud Gateway (16382.78 > 8425.73 > 7862.68)

    Nginx 性能大大领先,Zuul 优于 Spring Cloud Gateway

5. 第二轮

在上一轮测试中,我们发现,随着并发的上升,Spring Cloud Gateway 的 QPS 反倒逐步差于 Zuul ,这个是令我们非常意外的。毕竟 Spring Cloud Gateway 是 Spring Cloud 团队官方发布,并且未来是 Spring Cloud 主打的网关产品。况且 Netflix 团队,后来发布的 Zuul 2 ,也是基于非阻塞 IO 的方式实现的网关。

带着一脸懵逼,我去翻看了 Spring Cloud 官方开发提供的 spring-cloud-gateway-bench 的项目,其上面发布的测试结果,Spring Cloud Gateway 相比 Zuul 的测试性能,有近 50% 的提升。所以,按照它的方式,我们将 API 服务,替换成了 Static 服务,开始了本轮的测试。

当然,因为上一轮的 Nginx 性能非常优秀,我们在这一轮中,就不重复来跑了。

5.1 Static 服务

首先,获取 Static 服务:

git clone https://github.com/spencergibb/spring-cloud-gateway-bench.git

然后,启动 Static 服务:

cd spring-cloud-gateway-bench/static
nohup ./webserver &

最后,测试 Static 服务:

$ curl 127.0.0.1:8000/hello.txt

{output:"I Love Spring Cloud"}

下面,我们先来测试下,Static 服务在不同并发,性能的情况.

🚚 300 并发

$wrk -t50 -c300 -d30s -T30 http://127.0.0.1:8000/hello.txt

Running 30s test @ http://127.0.0.1:8000/hello.txt
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.61ms 4.27ms 49.12ms 69.77%
Req/Sec 1.16k 266.63 5.66k 70.81%
1741822 requests in 30.10s, 245.85MB read
Requests/sec: 57865.65
Transfer/sec: 8.17MB

  • QPS 为 57865.65 。
  • 平均延迟为 5.61ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s -T30 http://127.0.0.1:8000/hello.txt

Running 30s test @ http://127.0.0.1:8000/hello.txt
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 17.71ms 11.46ms 238.71ms 76.46%
Req/Sec 1.18k 221.06 7.95k 78.41%
1750853 requests in 30.09s, 247.12MB read
Requests/sec: 58180.49
Transfer/sec: 8.21MB

  • QPS 为 58180.49 。
  • 平均延迟为 17.71ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s -T30 http://127.0.0.1:8000/hello.txt

Running 30s test @ http://127.0.0.1:8000/hello.txt
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 55.74ms 30.87ms 922.71ms 78.74%
Req/Sec 1.09k 379.61 21.58k 89.30%
1585425 requests in 30.10s, 223.77MB read
Requests/sec: 52671.32
Transfer/sec: 7.43MB

  • QPS 为 52671.32 。
  • 平均延迟为 55.74ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8000/hello.txt

Running 30s test @ http://127.0.0.1:8000/hello.txt
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 87.86ms 41.12ms 937.19ms 78.31%
Req/Sec 1.07k 479.70 33.72k 86.43%
1558393 requests in 30.11s, 219.96MB read
Requests/sec: 51752.89
Transfer/sec: 7.30MB

  • QPS 为 51752.89 。
  • 平均延迟为 87.86ms 。

5.2 Spring Cloud Gateway

🚚 300 并发

$wrk -t50 -c300 -d30s http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 29.62ms 16.83ms 172.09ms 70.39%
Req/Sec 208.88 33.43 600.00 73.20%
312871 requests in 30.10s, 44.16MB read
Requests/sec: 10392.75
Transfer/sec: 1.47MB

  • QPS 为 10392.75 。
  • 平均延迟为 29.62ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 98.61ms 55.90ms 462.70ms 69.70%
Req/Sec 207.29 49.23 519.00 71.14%
304794 requests in 30.11s, 43.02MB read
Requests/sec: 10123.92
Transfer/sec: 1.43MB

  • QPS 为 10123.92 。
  • 平均延迟为 98.61ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 293.37ms 172.38ms 2.37s 71.16%
Req/Sec 191.76 93.74 1.04k 66.49%
283500 requests in 30.12s, 40.01MB read
Requests/sec: 9411.28
Transfer/sec: 1.33MB

  • QPS 为 9411.28 。
  • 平均延迟为 293.37ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 5000 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 549.86ms 351.91ms 3.50s 72.02%
Req/Sec 188.87 112.40 1.45k 67.46%
253979 requests in 30.15s, 35.85MB read
Requests/sec: 8424.33
Transfer/sec: 1.19MB

  • QPS 为 8424.33 。
  • 平均延迟为 549.86ms 。

5.3 Zuul

🚚 300 并发

$wrk -t50 -c300 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 300 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 44.20ms 51.78ms 711.17ms 87.13%
Req/Sec 195.42 56.70 1.07k 72.90%
291455 requests in 30.10s, 45.63MB read
Requests/sec: 9684.31
Transfer/sec: 1.52MB

  • QPS 为 9684.31 。
  • 平均延迟为 44.20ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 1000 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 106.95ms 45.24ms 811.67ms 88.20%
Req/Sec 191.83 31.48 1.44k 80.69%
287645 requests in 30.10s, 45.03MB read
Requests/sec: 9556.68
Transfer/sec: 1.50MB

  • QPS 为 9556.68 。
  • 平均延迟为 106.95ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 311.47ms 52.71ms 1.00s 85.88%
Req/Sec 191.75 50.94 1.73k 80.22%
287363 requests in 30.10s, 44.95MB read
Requests/sec: 9546.91
Transfer/sec: 1.49MB

  • QPS 为 9546.91 。
  • 平均延迟为 311.47ms 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/hello.txt
50 threads and 5000 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 515.74ms 70.34ms 1.22s 87.50%
Req/Sec 191.72 73.11 1.69k 81.85%
288443 requests in 30.10s, 45.11MB read
Requests/sec: 9583.11
Transfer/sec: 1.50MB

  • QPS 为 9583.11 。
  • 平均延迟为 515.74ms 。

5.4 小结

  • 300 并发时:Spring Cloud Gateway > Zuul (10392.75 > 9684.31)

    Spring Cloud Gateway 略微优于 Zuul

  • 1000 并发时:Spring Cloud Gateway > Zuul (10123.92 > 9556.68)

    Spring Cloud Gateway 略微优于 Zuul

  • 3000 并发时:Zuul > Spring Cloud Gateway (9546.91 > 8420.10)

    Zuul 优于 Spring Cloud Gateway

  • 5000 并发时:Zuul > Spring Cloud Gateway (9583.11 > 8424.33)

    Zuul 优于 Spring Cloud Gateway 。

结果发现,还是存在第一轮相同的问题,只是换成 Static 服务后,大家的 QPS 提高了。

6. 第三轮

艿艿:突然有种“这个糟老头,坏滴很”的感觉。

实际上,第一二轮,被代理请求的接口,都是基本没有任何 IO 操作,虽然说,Static 服务读取文件,但是因为是 SSD 硬盘,太快!再加上,网关和被代理的服务,都在本机,基本也没任何 IO 的损耗。总结来说,整个过程,就已经变成了纯 CPU 的过程了。

所以,本轮,我们来尝试,继续使用第一轮的 API 服务,增加一个“慢”接口,通过 sleep 100ms 来模拟。代码如下:

@GetMapping("/sleep")
public String sleep() throws InterruptedException {
Thread.sleep(100L);
return "sleep";
}

6.1 API 服务

🚚 300 并发

$wrk -t50 -c300 -d30s http://127.0.0.1:8080/sleep

Running 30s test @ http://127.0.0.1:8080/sleep
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 150.53ms 44.04ms 215.43ms 55.09%
Req/Sec 39.86 11.79 76.00 80.01%
59705 requests in 30.08s, 6.73MB read
Requests/sec: 1984.63
Transfer/sec: 228.97K

  • QPS 为 1984.63 。
  • 平均延迟为 150.53ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s http://127.0.0.1:8080/sleep

Running 30s test @ http://127.0.0.1:8080/sleep
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 498.42ms 71.98ms 617.29ms 88.25%
Req/Sec 40.67 23.46 191.00 73.05%
59817 requests in 30.09s, 6.73MB read
Requests/sec: 1987.79
Transfer/sec: 229.06KB

  • QPS 为 1987.79 。
  • 平均延迟为 498.42ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s http://127.0.0.1:8080/sleep

Running 30s test @ http://127.0.0.1:8080/sleep
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.46s 274.77ms 1.79s 94.15%
Req/Sec 95.45 112.44 590.00 81.85%
59924 requests in 30.10s, 6.74MB read
Requests/sec: 1990.83
Transfer/sec: 229.41KB

  • QPS 为 1990.83 。
  • 平均延迟为 1.46s 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s http://127.0.0.1:8080/sleep

Running 30s test @ http://127.0.0.1:8080/sleep
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.39s 525.23ms 2.77s 91.76%
Req/Sec 123.47 152.17 0.89k 84.97%
60020 requests in 30.09s, 6.75MB read
Requests/sec: 1994.53
Transfer/sec: 229.84KB

  • QPS 为 1994.53 。
  • 平均延迟为 2.39s 。

6.2 Spring Cloud Gateway

🚚 300 并发

$wrk -t50 -c300 -d30s http://127.0.0.1:8081/sleep

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 159.66ms 50.00ms 785.35ms 96.07%
Req/Sec 38.89 12.58 60.00 77.20%
56346 requests in 30.09s, 6.45MB read
Requests/sec: 1872.46
Transfer/sec: 219.43KB

  • QPS 为 1872.46 。
  • 平均延迟为 159.66ms 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s http://127.0.0.1:8081/sleep

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 491.88ms 58.63ms 1.54s 94.78%
Req/Sec 45.30 33.09 191.00 63.75%
59887 requests in 30.10s, 6.85MB read
Requests/sec: 1989.35
Transfer/sec: 233.13KB

  • QPS 为 1989.35 。
  • 平均延迟为 491.88ms 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s http://127.0.0.1:8081/sleep

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.44s 249.25ms 2.45s 88.39%
Req/Sec 58.75 65.30 550.00 88.85%
60109 requests in 30.09s, 6.88MB read
Requests/sec: 1997.64
Transfer/sec: 234.10KB

  • QPS 为 1997.64 。
  • 平均延迟为 1.44s 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8081/sleep

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.34s 537.41ms 3.80s 82.38%
Req/Sec 72.59 86.86 840.00 88.13%
59134 requests in 30.11s, 6.77MB read
Requests/sec: 1964.21
Transfer/sec: 230.18KB

  • QPS 为 1964.21 。
  • 平均延迟为 2.34s 。

小结

不同并发,QPS 基本和 API 服务 的 QPS 相同,2000 QPS 不到。也是比较合理的结果。

6.3 Zuul

🚚 300 并发

$wrk -t50 -c300 -d30s -T30 http://127.0.0.1:8081/sleep

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 300 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.61s 1.26s 11.80s 84.55%
Req/Sec 5.90 4.57 30.00 84.30%
5902 requests in 30.10s, 789.75KB read
Requests/sec: 196.08
Transfer/sec: 26.24KB

  • QPS 为 196.08 。
  • 平均延迟为 1.61s 。

🚚 1000 并发

$wrk -t50 -c1000 -d30s -T30 http://127.0.0.1:8081/sleep

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.60s 1.63s 18.32s 78.44%
Req/Sec 6.93 6.13 60.00 88.90%
5957 requests in 30.10s, 796.98KB read
Requests/sec: 197.91
Transfer/sec: 26.48KB

  • QPS 为 197.91 。
  • 平均延迟为 4.60s 。

🚚 3000 并发

$wrk -t50 -c3000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 3000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 11.15s 5.05s 21.90s 71.44%
Req/Sec 13.67 13.80 121.00 85.69%
5960 requests in 30.10s, 797.38KB read
Requests/sec: 198.01
Transfer/sec: 26.49KB

  • QPS 为 198.01 。
  • 平均延迟为 11.15s 。

🚚 5000 并发

$wrk -t50 -c5000 -d30s -T30 http://127.0.0.1:8081/hello

Running 30s test @ http://127.0.0.1:8081/sleep
50 threads and 5000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 14.30s 8.04s 28.97s 56.08%
Req/Sec 20.30 20.36 140.00 88.96%
5977 requests in 30.09s, 799.66KB read
Requests/sec: 198.62
Transfer/sec: 26.57KB

  • QPS 为 198.62 。
  • 平均延迟为 14.30s 。

小结

一脸懵逼 + 一脸懵逼 + 一脸懵逼?

我们来分析下,为什么会产生 QPS 不到 200 的原因。因为 Zuul 是阻塞 IO 的网关,那么在一个请求的过程中,线程就会一直阻塞着。那么,API 服务一个请求 sleep 了 100ms ,所以导致每秒只能处理 10 个请求。又因为 Zuul 默认配置下,每个路由仅允许 20 个请求( ZuulProperties.Host.maxPerRouteConnections ) ,所以结果就是 20 * 10 = 200 QPS 了。

当然,实际场景下,我们会调大 每个路由仅允许 20 个请求( ZuulProperties.Host.maxPerRouteConnections ),那这个时候,也是能达到不错的 QPS (此时,可能限制的条件,就是 Zuul 所运行的 Tomcat 的线程数了)。所以,还是我们在 《性能测试 —— SpringMVC、Webflux 基准测试》 提到的观点,非阻塞的特性,给我们带来的是,更好的将耗时的 IO 请求,丢到不同的线程池中。

666. 彩蛋

简单总结下:

  • Spring Cloud Gateway 因为其非阻塞 IO 的特性,作为网关,会比 Zuul 优秀很多,特别是后端 API 较慢的情况。
  • Nginx 的性能非常强劲,如果想要将 Nginx 作为网关,可以考虑使用 Openresty 或者 Kong ,当然需要团队有一定的 Lua 编程能力。
  • 如果是 Java 系为主的团队,想要以 Java 作为网关的开发,还是建议使用 Spring Cloud Gateway ,因为虽然测试结果不如 Zuul 理想(在高并发下),但是毕竟是 Spring Cloud 未来主打的 Gateway 选型,未来会越来越好。

推荐与参考文章:

文章目录
  1. 1. 1. 概述
  2. 2. 2. 性能指标
  3. 3. 3. 测试环境
  4. 4. 4. 第一轮
    1. 4.1. 4.1 API 服务
    2. 4.2. 4.2 Spring Cloud Gateway
    3. 4.3. 4.3 Zuul
    4. 4.4. 4.4 Nginx
    5. 4.5. 4.5 小结
  5. 5. 5. 第二轮
    1. 5.1. 5.1 Static 服务
    2. 5.2. 5.2 Spring Cloud Gateway
    3. 5.3. 5.3 Zuul
    4. 5.4. 5.4 小结
  6. 6. 6. 第三轮
    1. 6.1. 6.1 API 服务
    2. 6.2. 6.2 Spring Cloud Gateway
    3. 6.3. 6.3 Zuul
  7. 7. 666. 彩蛋