docs/distributed-system/api-gateway.md
API 网关(API Gateway)是位于客户端与后端服务之间的统一入口,所有客户端请求先经过网关,再由网关路由到具体的目标服务。
在微服务架构下,一个系统被拆分为多个服务。像安全认证、流量控制、日志、监控等功能是每个服务都需要的。如果没有网关,我们需要在每个服务中单独实现这些功能,导致:
网关的功能虽然繁多,但核心可以概括为两件事:
| 职责 | 说明 | 典型功能 |
|---|---|---|
| 请求转发 | 将客户端请求路由到正确的目标服务 | 动态路由、负载均衡、协议转换 |
| 请求过滤 | 在请求到达后端服务前/后进行拦截处理 | 身份认证、权限校验、限流熔断、日志记录 |
网关可以提供请求转发、安全认证(身份/权限认证)、流量控制、负载均衡、降级熔断、日志、监控、参数校验、协议转换等功能。
网关在微服务架构中的位置:所有客户端请求先到达网关,网关负责统一的认证鉴权、流量控制、路由分发,后端服务专注于业务逻辑处理。
引入网关后会增加一次网络转发(性能损耗在内网环境下通常可忽略),但同时也引入了新的单点风险。因此,网关服务本身必须保障高可用:
如下图所示,网关服务外层通过 Nginx(或其他负载均衡设备/软件)进行负载转发以达到高可用。Nginx 在部署时也应考虑高可用,避免单点风险。
绝大部分网关可以提供下面这些功能(有一些功能需要借助其他框架或者中间件):
下图来源于百亿规模 API 网关服务 Shepherd 的设计与实现 - 美团技术团队 - 2021这篇文章。
Zuul 是 Netflix 开发的一款提供动态路由、监控、弹性、安全的网关服务,基于 Java 技术栈开发,可以和 Eureka、Ribbon、Hystrix 等组件配合使用。
Zuul 核心架构如下:
Zuul 主要通过过滤器(类似于 AOP)来过滤请求,从而实现网关必备的各种功能。
我们可以自定义过滤器来处理请求,并且,Zuul 生态本身就有很多现成的过滤器供我们使用。就比如限流可以直接用国外朋友写的 spring-cloud-zuul-ratelimit (这里只是举例说明,一般是配合 hystrix 来做限流):
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
Zuul 1.x 基于同步 IO,性能较差。Zuul 2.x 基于 Netty 实现了异步 IO,性能得到了大幅改进。
重要提示:Spring Cloud 官方已在 Hoxton 版之后将 Zuul 1.x 移除。尽管 Netflix 开源了 Zuul 2.x,但 Zuul 2.x 并未被集成到 Spring Cloud 主流版本中。对于 Spring Cloud 技术栈的新项目,严禁选用 Zuul 1.x,推荐直接使用 Spring Cloud Gateway。
Spring Cloud Gateway 属于 Spring Cloud 生态系统中的网关,其诞生的目标是为了替代老牌网关 Zuul(准确说是 Zuul 1.x)。值得注意的是,Spring Cloud Gateway 的起步时间早于 Zuul 2.x,两者属于不同的技术演进路线。
| 版本 | IO 模型 | 线程模型 | 吞吐量 | 延迟 |
|---|---|---|---|---|
| Zuul 1.x | 同步阻塞(Servlet) | 每请求一线程 | 低 | 高 |
| Zuul 2.x | 异步非阻塞(Netty) | 事件循环 | 高 | 低 |
| Spring Cloud Gateway | 异步非阻塞(Netty) | 事件循环 | 高 | 低 |
Spring Cloud Gateway 基于 Spring WebFlux 实现,而不是传统的 Spring WebMVC。Spring WebFlux 使用 Reactor 库来实现响应式编程模型,底层基于 Netty 实现异步非阻塞的 I/O。
响应式编程的优势:
Spring Cloud Gateway 的核心组件包括三个部分:
Predicate 函数,用于匹配 HTTP 请求(如路径、方法、请求头等)GatewayFilter 的实例,用于在请求被发送到下游服务之前或之后修改请求和响应Spring Cloud Gateway 和 Zuul 2.x 都是通过过滤器来处理请求,但 Spring Cloud Gateway 与 Spring 生态系统(如 Eureka、Consul、Config)集成更加紧密。目前,对于 Java 技术栈的项目,Spring Cloud Gateway 是推荐的选择。
根据官方介绍:
OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty 基于 Nginx,主要还是看中了其优秀的高并发能力。不过,由于 Nginx 采用 C 语言开发,二次开发门槛较高。如果想在 Nginx 上实现一些自定义的逻辑或功能,就需要编写 C 语言的模块,并重新编译 Nginx。
为了解决这个问题,OpenResty 通过实现 ngx_lua 和 stream_lua 等 Nginx 模块,把 Lua/LuaJIT 完美地整合进了 Nginx,从而让我们能够在 Nginx 内部里嵌入 Lua 脚本,使得可以通过简单的 Lua 语言来扩展网关的功能,比如实现自定义的路由规则、过滤器、缓存策略等。
Lua 是一种非常快速的动态脚本语言,它的运行速度接近于 C 语言。LuaJIT 是 Lua 的一个即时编译器,它可以显著提高 Lua 代码的执行效率。LuaJIT 将一些常用的 Lua 函数和工具库预编译并缓存,这样在下次调用时就可以直接使用缓存的字节码,从而大大加快了执行速度。
关于 OpenResty 的入门以及网关安全实战推荐阅读这篇文章:每个后端都应该了解的 OpenResty 入门以及网关安全实战。
Kong 是一款基于 OpenResty (Nginx + Lua)的高性能、云原生、可扩展、生态丰富的网关系统,主要由 3 个组件组成:
Kong 早期确实依赖外部数据库存储配置,架构相对复杂,需要额外保障数据库层的高可用。但自 Kong 1.1 版本起,已支持 DB-less 模式(无库模式):
注意:本文后续讨论的 Kong 高可用问题,主要针对传统模式。在 K8s 环境使用 Ingress Controller 模式时,架构已大幅简化。
Kong 提供了插件机制来扩展其功能,插件在 API 请求响应循环的生命周期中被执行。比如在服务上启用 Zipkin 插件:
$ curl -X POST http://kong:8001/services/{service}/plugins \
--data "name=zipkin" \
--data "config.http_endpoint=http://your.zipkin.collector:9411/api/v2/spans" \
--data "config.sample_ratio=0.001"
Kong 本身就是一个 Lua 应用程序,并且是在 Openresty 的基础之上做了一层封装的应用。归根结底就是利用 Lua 嵌入 Nginx 的方式,赋予了 Nginx 可编程的能力,这样以插件的形式在 Nginx 这一层能够做到无限想象的事情。例如限流、安全访问策略、路由、负载均衡等等。编写一个 Kong 插件,就是按照 Kong 插件编写规范,写一个自己自定义的 Lua 脚本,然后加载到 Kong 中,最后引用即可。
除了 Lua,Kong 还可以基于 Go 、JavaScript、Python 等语言开发插件,得益于对应的 PDK(插件开发工具包)。
关于 Kong 插件的详细介绍,推荐阅读官方文档:https://docs.konghq.com/gateway/latest/kong-plugins/,写的比较详细。
APISIX 是一款基于 OpenResty 和 etcd 的高性能、云原生、可扩展的网关系统。
etcd 是使用 Go 语言开发的一个开源的、高可用的分布式 key-value 存储系统,使用 Raft 协议做分布式共识。
与传统 API 网关相比,APISIX 具有动态路由和插件热加载,特别适合微服务系统下的 API 管理。并且,APISIX 与 SkyWalking(分布式链路追踪系统)、Zipkin(分布式链路追踪系统)、Prometheus(监控系统) 等 DevOps 生态工具对接都十分方便。
作为 Nginx 和 Kong 的替代项目,APISIX 目前已经是 Apache 顶级开源项目,并且是最快毕业的国产开源项目。国内目前已经有很多知名企业(比如金山、有赞、爱奇艺、腾讯、贝壳)使用 APISIX 处理核心的业务流量。
根据官网介绍:“APISIX 已经生产可用,功能、性能、架构全面优于 Kong”。
APISIX 同样支持定制化的插件开发。开发者除了能够使用 Lua 语言开发插件,还能通过下面两种方式开发来避开 Lua 语言的学习成本:
Wasm 是基于堆栈的虚拟机的二进制指令格式,一种低级汇编语言,旨在非常接近已编译的机器代码,并且非常接近本机性能。Wasm 最初是为浏览器构建的,但是随着技术的成熟,在服务器端看到了越来越多的用例。
Shenyu 是一款基于 WebFlux 的可扩展、高性能、响应式网关,Apache 顶级开源项目。
Shenyu 通过插件扩展功能,插件是 ShenYu 的灵魂,并且插件也是可扩展和热插拔的。不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发、重写、重定向、和路由监控等插件。
| 特性 | Zuul 1.x | Zuul 2.x | Spring Cloud Gateway | Kong | APISIX | Shenyu |
|---|---|---|---|---|---|---|
| IO 模型 | 同步阻塞 | 异步非阻塞 | 异步非阻塞 | 异步非阻塞 | 异步非阻塞 | 异步非阻塞 |
| 底层技术 | Servlet | Netty | WebFlux + Netty | OpenResty (Nginx + Lua) | OpenResty + etcd | WebFlux + Netty |
| 性能 | 低 | 高 | 高 | 很高 | 很高 | 高 |
| 动态配置 | 需重启 | 支持 | 支持 | 支持 | 支持(热更新) | 支持 |
| 配置存储 | 内存 | 内存 | 内存 | 数据库 / YAML / K8s CRD | etcd(分布式) | 内存/数据库 |
| 限流熔断 | 需集成 | 需集成 | 内置(集成 Resilience4j) | 插件 | 插件 | 插件 |
| 生态系统 | Netflix | Netflix | Spring Cloud | CNCF / Kong | Apache | Apache |
| 运维复杂度 | 低 | 中 | 低 | 中(DB-less) / 高(DB Mode) | 中 | 中 |
| 学习曲线 | 平缓 | 平缓 | 平缓 | 陡峭(Lua) | 陡峭(Lua) | 平缓(Java) |
| 适用场景 | 遗留系统 | Netflix 技术栈 | Spring Cloud 生态 | 云原生、多语言 | 云原生、高性能 | Java 生态 |
选择 API 网关需要综合考虑技术栈、性能要求、团队能力和运维成本。
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| Spring Cloud 生态 | Spring Cloud Gateway | 与 Spring Boot/Spring Cloud 无缝集成,配置简单 |
| 高性能 / 云原生 | APISIX | 基于 etcd 的热更新、性能优异、云原生架构 |
| 多语言生态 | Kong | 插件丰富、支持多语言开发、社区成熟 |
| Netflix 技术栈 | Zuul 2.x | 与 Eureka、Ribbon、Hystrix 等组件无缝配合 |
| 双层架构(推荐) | Kong/APISIX(流量网关) + Spring Cloud Gateway(业务网关) | 流量网关处理 SSL、WAF、全局限流;业务网关处理微服务鉴权、参数聚合 |