API网关的技术形态和发展趋势
“Any problem in computer science
can be solved
by anther layer of indirection.”
— David Wheeler
在软件架构设计中很多都采用“层”的结构,所以有这样一句名言:“计算机领域的任何问题都可以通过增加一个间接的中间层来解决”。API网关便是这样一个中间层,无论是早期银行证券系统的前置机,还是随着微服务架构模式发展而演化出来的API网关,它都是作为API访问的统一接入的中间层。今天我们从以下四点来聊一聊API网关:
微服务经典的四层架构
-
API网关模式 -
常见的开源API网关
网关在容器技术和K8S主导的云原生时代将如何演进
1. 微服务经典的四层架构
单体应用中有一个典型的MVC分层技术,把系统分为数据层、展示层和业务逻辑层,在这种模式下我们比较关注应用自身。而到微服务时期,每个微服务除了数据和业务逻辑之外,往往要依赖下游服务的API,同时自身也会提供API给上游服务,微服务之间相互协作提供整体的系统能力。此时,除了微服务本身之外更加关注整个上下游,为了统一API接入和避免公共能力的重复建设,设计了网关路由层;为了更加解耦,不至于微服务牵一发而动全身,又独立出一个BFF层(Backend For Front)。这就是典型的四层架构:前端层、网关层、BFF层、微服务层。借用netflix的系统架构为例来说明一下,早期netflix四层架构如下图:
随着业务的更加复杂,边界API层一方面随着微服务层的变动而频繁修改,给开发测试带来巨大的工作量,于是独立出一层隔离变化。
无论是四层还是五层,netflix服务入口流量都是由zuul一夫当关,作为边缘网关,将外部流量调度到内部服务,俗称南北流量调度。当然,边缘网关作为外部流量进入内部服务的第一道“关卡”,天然适合承载安全认证、流量控制等通用功能,这也就引出了另一个话题:API网关模式!
2. API网关模式
|
其他功能 |
|
|
流量网关:跟具体的后端业务服务完全无关,比如安全策略、全局性流控策略、流量分发策略等,其功能跟 Web 应用防火墙(WAF)非常类似,可基于 Nginx/OpenResty 的 ngx_lua 模块开发。
业务网关:与后端服务和业务有一定关联性,并且一般被直接部署在业务服务的前面。业务网关一般部署在流量网关之后,业务系统之前,比流量网关更靠近服务。我们大部分情况下说的API网关,狭义上指的是业务网关。并且如果系统的规模不大,我们也会将两者合二为一,使用一个网关来理所有的工作。
如果百度谷歌一下的话会发现可选择的API网关灰常多,按语言分类常见的开源网关大概有五大类:
语 言 |
开源网关 |
Nginx + Lua |
OpenResty, Kong, Orange, ABTesting gateway等 |
Java |
Zuul/Zuul2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul等 |
Go |
Janus、fagongzi、Grpc-gateway,Gloo,istio gateway等 |
.net |
Ocelot |
nodejs |
Express Gateway、Micro Gateway |
OpenResty
Kong
zuul
Spring Cloud Gateway
3.1 Openresty
3.3 Zuul
3.4 Spring Cloud Gateway
3.5 如何选择?
kong 性能优秀,非常适合做流量网关,同样是基于nginx和lua,Kong在openresty基础上做了很多扩展,对于 service、route、upstream、consumer、plugins 的抽象,也是自研网关值得借鉴的。但是,对于复杂系统,不建议业务网关用 Kong,或者更明确的说是不建议在 Java 技术栈的系统深度定制 Kong 或 OpenResty,主要是工程性方面的考虑。毕竟维护lua脚本的工作量和成本不低。
Spring Cloud Gateway对于 Java 技术栈来说比较方便,可以复用业务系统的一些组件。Lua 不方便,不光是语言的问题,更是复用基础设施的问题。另外,对于网关系统来说,性能不是差一个数量级,问题不大,多加 2 台机器就可以搞定。目前来看 Zuul2 的坑还是比较多的,因此作为java技术栈,比较建议使用 Spring Cloud Gateway 作为基础骨架。
4.2 Nginx Ingress
把上面Ingress原理图中的反向代理换成nginx,再配合Nginx Ingress Controller来动态调整配置便是Nginx Ingress,其工作原理可以简单描述为:Nginx ingress控制器通过和APIServer交互,动态感知集群中ingress规则变化,然后读取它并生成的Nginx配置写入nginx的/etc/nginx.conf文件中,reload使配置生效。以此达到域名分配置和动态更新的问题。下面是一个网上找的示意图,图中右下角只画了iptable来规则来选择pod并不完全,但不妨碍我们理解nginx ingress的工作原理。详细的信息可以从这里了解:https://kubernetes.github.io/ingress-nginx/
作为中心代理,代理集群的南北流量,也就是入口流量,负责流量分发、流量治理类的工作。这种模式下,envoy一般就是负载均衡设备或者api网关的基础数据面,在非istio场景里比较有代表的ambassador, gloo。
作为业务进程的sidecar。当有业务请求访问业务的时候,流量会劫持到sidecar envoy中,之后再转发到业务进程。当业务访问其他业务时,请求流量会被拦截到sidecar envoy中,再被转发到目标服务。
Istio Ingress Gateway是Envoy第一种模式的代理封装,在service mesh环境中也是一个不错的选择。使用Istio Gateway(Envoy)做南北流量调度,同时使用sidecar(Envoy)做东西流量治理。下面是一个示意图:
选择这种方案的一个好处是统一到envoy,前提是你的基础设施底座已经具备服务网格这个条件。另一个好处,如果你们微服务场景下使用了不同的语言和技术栈去实现特定的服务,这种方案可以做到技术栈解耦。但用istio ingress gateway做网关也有局限性:它比较适合做基础的流量控制,比如限流/重试/熔断等,而和业务相关的比如身份认证、鉴权等比较难做。当然如果团队有这个技术栈,能做基于envoy(c++技术栈)的扩展研发,或许可以定制一些插件。否则通用的基础流量调度能力放在envoy,业务网关还需要选择另一个方案,两种方案并存会增加不少复杂性。
除了上面介绍的几个云原生环境里基于K8S的网关方案之外,还有不少其他方案,比如ambassador, gloo,其实前文写到的kong也是。业界有不少在K8S集群基于服务网格的网关探索和实践的案例,比如蚂蚁无线网关基于MOSN的mesh化改造,再比如网易基于envoy的网关演进等等。
如果说上面一些方案只是技术底座的不同,并没有改变网关的属性和功能定义,也没有改变传统的流量网关和微服务网关的双层模型来分别做南北流量和东西流量的调度。那么新的一种探索趋势的确是东西南北流量调度以及服务治理合二为一,以此减低资源使用和运维成本。下图是网上找的合二为一的网关示意图,这大概也许就是对云原生网关的最新定义。