从几方面说一下:
1、功能
功能上说,这两个框架挺像的,一个是基于zuul的同步io方式的改进,产生了zuul2,一半代码都是在封装和定制netty本身。另一个是基于spring 5 webflux,底层也是netty,大家的起点是一样的。
Zuul 目前有两个大的版本,1.x 和 2.x,这两个版本差别很大。
Zuul 1.x 基于同步 IO,也是 Spring Cloud 全家桶的一部分,可以方便的配合 Spring Boot/Spring Cloud 配置和使用。
在 Zuul 1.x 里,filter 的种类和处理流程可以参见下图,最主要的就是 pre、routing、post 这三种过滤器,分别作用于调用业务服务 API 之前的请求处理、直接响应、调用业务服务 API 之后的响应处理。
(Zuul 1.x 示意图)
Zuul 2.x 最大的改进就是基于 Netty Server 实现了异步 IO 来接入请求,同时基于 Netty Client 实现了到后端业务服务 API 的请求。这样就可以实现更高的性能、更低的延迟。此外也调整了 filter 类型,将原来的三个核心 filter 显式命名为:Inbound Filter、Endpoint Filter 和 Outbound Filter。
(Zuul 2.x 示意图)
Zuul 2.x 核心功能:
- Service Discovery
- Load Balancing
- Connection Pooling
- Status Categories
- Retries
- Request Passport
- Request Attempts
- Origin Concurrency Protection
- HTTP/2
- Mutual TLS
- Proxy Protocol
- GZip
- WebSockets
Spring Cloud Gateway
项目地址:
https://github.com/spring-cloud/spring-cloud-gateway/
Spring Cloud Gateway 基于 Java 8、Spring 5.0、Spring Boot 2.0、Project Reactor,发展的比 Zuul 2 要早,目前也是 Spring Cloud 全家桶的一部分。
Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。
核心特性:
- Able to match routes on any request attribute.
- Predicates and filters are specific to routes.
- Hystrix Circuit Breaker integration.
- Spring Cloud DiscoveryClient integration
- Easy to write Predicates and Filters
- Request Rate Limiting
- Path Rewriting
四大开源网关的对比分析(OpenResty/Kong/Zuul2/SpringCloudGateway 等)
OpenResty/Kong/Zuul2/SpringCloudGateway 重要特性对比
以限流功能为例:
- Spring Cloud Gateway 目前提供了基于 Redis 的 Ratelimiter 实现,使用的算法是令牌桶算法,通过 yml 文件进行配置;
- Zuul2 可以通过配置文件配置集群限流和单服务器限流亦可通过 filter 实现限流扩展;
- OpenResty 可以使用 resty.limit.count、resty.limit.conn、resty.limit.req 来实现限流功能可实现漏桶或令牌通算法;
- Kong 拥有基础限流组件,可在基础组件源代码基础上进行 lua 开发。
对 Zuul/Zuul2/Spring Cloud Gateway 的一些功能点分析可以参考 Spring Cloud Gateway 作者 Spencer Gibb 的文章:
https://spencergibb.netlify.com/preso/detroit-cf-api-gateway-2017-03/
2、性能
(Spring Cloud Gateway、Zuul2、OpenResty、Kong 的性能对比)
上图中 y 轴坐标是 QPS,x 轴是一个 Gateway 的数据,每根线是一个场景下的不同网关数据,测试结论如下:
- 实测情况是性能 SCG~Zuul2 << OpenResty ~< Kong << Direct(直连);
- Spring Cloud Gateway、Zuul2 的性能差不多,大概是直连的40%;
- OpenResty、Kong 差不多,大概是直连的 60-70%;
- 大并发下,例如模拟 200 并发用户、1000 并发用户时,Zuul2 会有很大概率返回出错。
对于一般的系统来说,这些性能指标也远大于业务接口本身的TPS,其实完全够用。只是我们当时的业务系统对延迟有较高要求,所以发现了SpringCloudGateway的延迟平均数跟Zuul2一样,但是P95,P99差别较大(延迟分布的95%以上的部分),是Zuul2的3倍以上。我提交了这个BUG issue,最开始SpringCloudGateway负责人Spencer BB不同意这个bug,后来其他人陆续反馈,并且发现是依赖的WebFlux依赖的netty-reactor的bug问题,后续版本修复了。。。也算是为这个框架做了一点贡献。
3、其他
开源网关的测试分析
脱离场景谈性能,都是耍流氓。性能就像温度,不同的场合下标准是不一样的。同样是 18 摄氏度,老人觉得冷,小孩觉得很合适,企鹅觉得热,冰箱里的蔬菜可能要坏了。
同样基准条件下,不同的参数和软件,相对而言的横向比较,才有价值。比如同样的机器(比如 16G 内存/4Core),同样的 server(用 Spring Boot,配置路径 api/hello 返回一个 helloworld),同样的压测方式和工具(比如用 WRK,10 线程,20 并发连接),我们测试直接访问 server 得到的极限 QPS(QPS-Direct,29K);和配置了一个 Spring Cloud Gateway 做网关访问的极限 QPS(QPS-SCG,11K)、同样方式配置一个 Zuul2 做网关压测得到的极限 QPS(QPS-Zuul2,13K),Kong 得到的极限 QPS(QPS-Kong,21K),OpenResty 得到的极限 QPS(QPS-OR,19K),这个对比就有意义了。
Kong 的性能非常不错,非常适合做流量网关,并且对于 service、route、upstream、consumer、plugins 的抽象,也是自研网关值得借鉴的。
对于复杂系统,不建议业务网关用 Kong,或者更明确的说是不建议在 Java 技术栈的系统深度定制 Kong 或 OpenResty,主要是工程性方面的考虑。举个例子:假如我们有很多个不同业务线,鉴权方式五花八门,都是与业务多少有点相关的。这时如果把鉴权在网关实现,就需要维护大量的 Lua 脚本,引入一个新的复杂技术栈是一个成本不低的事情。
Spring Cloud Gateway/Zuul2 对于 Java 技术栈来说比较方便,可以依赖业务系统的一些 common jar。Lua 不方便,不光是语言的问题,更是复用基础设施的问题。另外,对于网关系统来说,性能不是差一个数量级,问题不大,多加 2 台机器就可以搞定。
目前测试的总结来看,如果服务都是 2ms 级别,直连的性能假如是 100,Kong 可以到 60,OpenResty 是 50,Zuul2 和 Spring Cloud Gateway 是 35,如果服务本身的 latency 大一点,这些个差距会逐步缩小。
目前来看 Zuul2 的坑还是比较多的:
- 不成熟,没文档,刚出不久,还没有太多的实际应用案例
- 高并发时出错率较高,1000 并发时我们的测试场景近 50% 的出错
所以简单使用或者轻度定制业务网关系统,目前比较建议使用 Spring Cloud Gateway 作为基础骨架。