共计 1928 个字符,预计需要花费 5 分钟才能阅读完成。
用于实现 API 网关的技术有很多,大致分为这么几类:
通用反向代理:Nginx、Haproxy、……
网络编程框架:Netty、Servlet、……
API 网关框架:Spring Cloud Gateway、Zuul、Zuul2、……
API 网关最基本的功能就是反向代理,所以在对 API 网关做技术选型的时候需要着重考察其性能表现,本文对 Nginx、Haproxy、Netty、Spring Cloud Gateway、Zuul2 做了性能测试,测试代码可以在 github 获得。
测试方法
准备了三台 2CPU 4G 内存的服务器,分别运行 Tomcat、API Gateway、Gatling(压测工具)
先对 Tomcat 做压测,取 Tomcat 充分预热后的压测结果作为基准。压的是 Tomcat 自带的 example:/examples/jsp/jsp2/simpletag/book.jsp
在对 Netty、Zuul2、Spring Cloud Gateway 做压测时候也是先压个几轮做预热。
被测的 API 网关都没有添加额外业务,只做反向代理
吞吐量
下图是吞吐量的情况,可以看到 Netty、Nginx、Haproxy 均比直压 Tomcat 低一点点,而 Spring Cloud Gateway 和 Zuul2 则要低得多。
下面这张图可以更明显的看到吞吐量比较,Tomcat 为 100% 因为它是基准值,Netty、Nginx、Haproxy 的只比基准值低 8%,而 Spring Cloud Gateway 和 Zuul2 则只是基准值的 35% 和 34%(难兄难弟)。
平均响应时间
下图可以看到 Netty、Nginx、Haproxy 的平均响应时间与 Tomcat 差不多。但是 Spring Cloud Gateway 和 Zuul2 则是 Tomcat 的 3 倍多,不出所料。
下图同样是以 Tomcat 作为基准值的比较:
响应时间分布
光看平均响应时间是不够的,我们还得看 P50、P90、P99、P99.9 以及 Max 响应时间(可惜 Gatling 只能设置 4 个百分位,否则我还想看看 P99.99 的响应时间)。
为何要观察 P99.9 的响应时间?光看 P90 不够吗?理由有两个:1)观察 P99、P99.9、P99.99 的响应时间可以观察系统的在高压情况下的稳定性,如果这三个时间的增长比较平滑那么说明该系统在高压力情况下比较稳定,如果这个曲线非常陡峭则说明不稳定。
2)观察 P99、P99.9、P99.99 的响应时间能够帮助你估算用户体验。假设你有一个页面会发出 5 次请求,那么这 5 次请求均落在 P90 以内概率是多少?90%^5=59%,至少会经历一次 > P90 响应时间的概率是 100%-59%=41%,如果你的 P90=10s,那么就意味着用户有 41% 的概率会在加载页面的时候超过 10s,是不是很惊人?如果你的 P99=10s,那么用户只有 5% 的概率会在访问页面的时候超过 10s。如果 P99.9=10s,则有 0.4% 的概率。
关于如何正确测量系统可以看“How NOT to Measure Latency”by Gil Tene
下面同样是把结果与 Tomcat 基准值做对比:
可以看到几个很有趣的现象:
Haproxy、Nginx 的 P50、P90、P99、P99.9、Max 都是逐渐递增的。
Netty 的 P50、P90、P99、P99.9 是很平坦的,Max 则为基准值的 207%。
Spring Cloud Gateway 和 Zuul2 则是相反的,它们的平面呈现下降趋势。Spring Cloud Gateway 的 Max 甚至还比基准值低了一点点(94%),我相信这只是一个随机出现的数字,不要太在意。
结论
Nginx、Haproxy、Netty 三者的表现均很不错,其对于吞吐量和响应时间的性能损耗很低,可以忽略不计。
但是目前最为火热的 Spring Cloud Gateway 和 Zuul2 则表现得比较糟糕,因我没有写额外的业务逻辑这,可以推测这和它们的内置逻辑有关,那么大致有这么几种可能:
内置逻辑比较多
内置逻辑算法存在问题,占用了太多 CPU 时间
内置逻辑存在阻塞
内置逻辑没有用正确姿势使用 Netty(两者皆基于 Netty)
不管是上面的哪一种都需要再后续分析。
不过话说回来考虑选用那种作为 API 网关(的基础技术)不光要看性能,还要看:
是否易于扩展自己的业务逻辑
API 使用的便利性
代码的可维护性
文档是否齐全
…
性能只是我们手里的一个筹码,当我们知道这个东西性能到底几何后,才可以与上面的这些做交换(trade-off)。比如 Nginx 和 Haproxy 的可扩展性很差,那么我们可以使用 Netty。如果你觉得 Netty 的 API 太底层了太难用了,那么可以考虑 Spring Cloud Gateway 或 Zuul2。前提是你知道你会失去多少性能。