Spring Cloud GatewaySpring 官方为了完善 Spring Cloud 的版图 而推出的网关服务组件,使用了非阻塞式网络模式和目前流行的响应式编程模型,吸引了很多公司和开发者的注意 力。

网络上有一些现成的对 Spring Cloud Gateway 的性能测试的案例,比如 纠错帖:Zuul & Spring Cloud Gateway & Linkerd性能对比。根据 Simple benchmark comparing zuul and spring cloud gateway 的数据,Spring Cloud Gateway 的性能测试结果参考如下:

ProxyAvg LatencyAvg Req/Sec/Thread
gateway6.61ms3.24k
linkered7.62ms2.82k
zuul12.56ms2.09k
none2.09ms11.77k

但是,由于:

  1. 上述测试只是简单的对后端反向代理测试,而我们知道 Spring Cloud Gateway 对于路由的匹配是顺序的,匹配链后面的路由的性能并没有被关注到。
  2. 测试数据被进行了简化,测试的细节被(有意地)忽略了。

所以,在根据 Spring Cloud Gateway 改造出了初版的公司网关系统之后,我使用 wrkwrk2 对网关进行了多轮的压测,测试结果汇总为本文。

🔗Overview

为测试选择 3 个对照组:

  • direct 组,直接访问后端服务。
  • api0 组,通过网关访问后端服务,路由在匹配链的第 1 个被匹配到。
  • api800 组,直接访问后端服务,路由在匹配链的第 800 个被匹配到。
  1. Round0: 使用 wrk 针对多个不同的并发度(-c 选项)进行压测,找到能正确返回的最大并发度和其 时的 QPS。
  2. Round1: 使用 wrk2 针对上一步得到的并发度,按一定步长使用多个负载(-R 选项)测试得到请求 延时,为了快速定位到合适的负载,设置步长为 5000。
  3. Round2: 同 Round1,但此时只对 api800 组做测试且可以根据 Round1 的结果选择较小的步长 200。
  4. Round3: 同 Round1,但此时只对 api0 组做测试且可以根据 Round1 的结果选择较小的步长 200。
  5. Round4: 根据 Round1 得到的并发度和 Round3 得到的 QPS,对 api0 组重复做 3 组测试。
  6. Round5: 根据 Round1 得到的并发度和 Round2 得到的 QPS,对 api800 组重复做 3 组测试。

测试使用的后端服务是一个基于 Vert.x Web 的非常简单的 HTTP 服务,部署于公司的私有云平台上,配置为 4核 4G 内存,访问端口为 10.144.69.53:8080;网关服务器基于 Spring Cloud Gateway Greenwich.SR2 版本,Spring Boot 的版本为 2.1.6.RELEASE,同样部署于公司的私有云平台,配置为 8核 5G 内存,访问端口为 10.145.67.20:8080;压测的客户端程序运行于一台物理机服务器上,配置为 32核 62G 内存。

🔗Round0

使用 wrk 设置选项 -c 值分别为:

  • 100
  • 300
  • 500
  • 700
  • 900
  • 1100
  • 1300
  • 1500
  • 1700
  • 1900
  • 2100
  • 2300
  • 2500
  • 2700
  • 2900

对所有组进行压测。

wrk -t 16 -d 60s --latency -c ${each_c} ${endpoint}

得到结论:

  • direct 组最多能到 500 并发连接
  • api0 组最多能到 300 并发连接
  • api800 组最多能到 100 并发连接

测试脚本及数据可供下载

🔗Round1

使用 wrk2 设置选项 -c 值分别为:

  • 100
  • 200
  • 300

设置选项 -R 值分别为:

  • 100
  • 1000
  • 5000
  • 10000
  • 15000
  • 20000
  • 25000
  • 30000

对所有组进行压测。

wrk -t 16 -d 60s --latency -c ${each_c} -R ${each_r} ${endpoint}

得到结论:

  • direct 组最多能到 30000 以上的 QPS
  • api0 组最多能到 10000 以上 15000 以下的 QPS
  • api800 组最多能到 1000 以上 5000 以下的 QPS

🔗Round2

使用 wrk2 设置选项 -c 值分别为:

  • 100
  • 200
  • 300

设置选项 -R 值分别为:

  • 1000
  • 1200
  • 1400
  • 1600
  • 1800
  • 2000
  • 2200
  • 2400
  • 2600

api800 组进行压测。

wrk -t 16 -d 60s --latency -c ${each_c} -R ${each_r} ${endpoint}

得到结论:

  • api800 组最多能到 2000 以上 2200 以下的 QPS

🔗Round3

使用 wrk2 设置选项 -c 值分别为:

  • 100
  • 200
  • 300

设置选项 -R 值分别为:

  • 10000
  • 10200
  • 10400
  • 10600
  • 10800
  • 11000
  • 11200
  • 11400
  • 11600
  • 11800
  • 12000
  • 12200
  • 12400
  • 12600
  • 12800
  • 13000

api0 组进行压测。

wrk -t 16 -d 60s --latency -c ${each_c} -R ${each_r} ${endpoint}

得到结论:

  • api0 组最多能到 11000 以上 11200 以下的 QPS

🔗Round4

使用 wrk2 设置选项 -R 值分别为:

  • 10900
  • 11000
  • 11100

api0 组进行重复的 3 次压测。

wrk -t 16 -d 60s --latency -c 300 -R ${each_r} ${endpoint}

得到请求延时结果如下:

KEYQPSTP90TP99TP999MEANSTDEV
10900.r_010878.7635.29ms83.65ms232.57ms14.79ms20.57ms
10900.r_110894.4037.98ms95.04ms300.80ms16.62ms23.66ms
10900.r_210893.9737.92ms86.27ms253.44ms16.47ms21.83ms
11000.r_010992.2638.46ms68.22ms108.86ms16.37ms16.47ms
11000.r_110988.9739.39ms66.37ms94.53ms16.95ms16.33ms
11000.r_210988.1440.00ms73.54ms140.93ms17.48ms18.04ms
11100.r_011081.1646.46ms121.47ms395.52ms22.75ms30.49ms
11100.r_111089.5948.74ms115.14ms349.70ms23.86ms28.25ms
11100.r_211090.9252.26ms245.50ms811.52ms28.82ms55.56ms

🔗Round5

使用 wrk2 设置选项 -R 值分别为:

  • 1900
  • 2000
  • 2100

api800 组进行重复的 3 次压测。

wrk -t 16 -d 60s --latency -c 300 -R ${each_r} ${endpoint}

得到请求延时结果如下:

KEYQPSTP90TP99TP999MEANSTDEV
1900.r_01898.1640.00ms68.61ms90.05ms14.36ms16.23ms
1900.r_11899.9339.55ms69.50ms98.50ms14.60ms15.97ms
1900.r_21899.6142.33ms77.69ms106.37ms15.94ms17.42ms
2000.r_01999.9843.46ms70.08ms91.97ms15.92ms17.25ms
2000.r_12000.4652.58ms93.69ms174.21ms20.32ms22.93ms
2000.r_21999.9950.08ms83.20ms113.92ms18.90ms20.27ms
2100.r_02059.211.79s5.75s7.88s716.84ms1.09s
2100.r_12068.651.34s4.48s6.99s535.81ms829.71ms
2100.r_22060.511.49s4.81s7.57s602.15ms925.10ms

🔗Round X

作为对照,使用 wrk2 设置选项 -R 值分别为:

  • 100
  • 1000
  • 10000

对所有组进行压测。

wrk -t 16 -d 60s --latency -c 100 -R ${each_r} ${endpoint}
KEYQPSTP90TP99TP999MEANSTDEV
100.direct100.641.54ms1.91ms2.17ms0.97ms410.39us
100.api0100.742.32ms31.85ms51.97ms2.20ms4.35ms
100.api800100.7418.48ms34.88ms42.24ms7.77ms6.99ms
1000.direct1000.231.49ms1.95ms2.20ms0.86ms458.60us
1000.api01000.232.33ms3.02ms21.26ms1.81ms1.19ms
1000.api800998.7213.99ms34.24ms43.33ms7.13ms6.39ms
10000.direct9998.381.50ms2.06ms6.43ms0.96ms682.08us
10000.api09998.222.76ms22.40ms45.50ms2.57ms3.94ms
10000.api8002040.3843.78s47.38s48.23s27.86s11.48s

🔗Conclusion

Spring Cloud Gateway 在匹配到前几位的路由时性能还可以接受,当需要经过较多次的路由匹配时,性能下降明显。

可能的优化点应该是针对不同的匹配类型使用前缀树等来提高匹配速度,而不能使用链式的匹配模型。

🔗Download

所有的测试相关的脚本和数据可供下载。


以上。