本次分享我们从第七届MAD技术论坛-《API网关的设计与实现记录》API 网关(API Gateway)的现状和面临问题出发,阐述微服务架构与 API 网关的关系,理顺流量网关与业务网关的脉络。
原视频地址:第七届MAD技术论坛——《百亿流量API网关设计与实现》
计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。 —— David Wheeler
API Gateway与综合前置、ESB
其实网关跟面向服务架构(Service Oriented Architecture,SOA)和微服务架构(MicroServices Architecture,MSA)有很深的渊源。
十多年以前,银行等金融机构完成全国业务系统大集中以后,分散的系统都变得集中,同时也带来各种问题:业务发展过快如何应对,对接系统过多如何集成和管理。为了解决这些问题,业界实现了作用于渠道与业务系统之间的中间层网关,即综合前置系统,由其适配各类渠道和业务,处理各种协议接入、路由与报文转换、同步异步调用等。
人们基于SOA的理念,在综合前置的基础上,进一步增加了服务的元数据管理、注册、中介、编排、治理等功能,逐渐形成了企业服务总线(ESB,EnterpriseService Bus)。
PS:当然基于 ESB 这种集中式管理的 SOA 方案也存在着种种问题,特别是面向互联网技术领域的爆发式发展的情况下。
1.2.1 使用特点:
API网关面对大流量访问一定需要对其分级,区分内部访问还是外部方位,以及每个系统访问的限流。
1.2.2面临的问题:
近年来,随着互联网技术的飞速发展,为了解决以ESB为代表的集中式管理的SOA方案的种种问题,以Apache Dubbo(2011年开源后)与Spring Cloud为代表的分布式服务化技术的出现,给了SOA实现的另外一个选择:去中心化的分布式服务架构(DSA)。分布式服务架构技术不再依赖于具体的服务中心容器技术(比如ESB),而是将服务寻址和调用完全分开,这样就不需要通过容器作为服务代理。
之后又在此基础上随着REST、Docker容器化、领域建模、自动化测试运维等领域的发展,逐渐形成了微服务架构(MSA)。在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立地设计、开发、测试、部署和管理。这时,各个独立部署单元可以选择不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,但是要求必须使用一种语言和平台无关的服务协议作为各个单元之间的通信方式。
在微服务架构中,由于系统和服务的细分,导致系统结构变得非常复杂,RESTAPI由于其简单、高效、跨平台、易开发、易测试、易集成,成为不二选择。此时一个类似综合前置的系统就产生了,这就是API网关(API Gateway)。API网关作为分散在各个业务系统微服务的API聚合点和统一接入点,外部请求通过访问这个接入点,即可访问内部所有的REST API服务。
将所有需要考虑的问题和功能可以分为两类:
全局性的,跟具体的后端业务系统和服务完全无关的部分,比如安全策略、全局性流控策略、流量分发策略等。
针对具体的后端业务系统,或者是服务和业务有一定关联性的部分,并且一般被直接部署在业务服务的前面。
随着互联网的复杂业务系统的发展,这两类功能集合逐渐形成了现在常见的两种网关系统:流量网关和业务网关
我们定义全局性的、跟具体的后端业务系统和服务完全无关的策略网关,即为流量网关。这样流量网关关注全局流量的稳定与安全,比如防止各类SQL注入、黑白名单控制、接入请求到业务系统的负载均衡等,通常有如下通用性的具体功能:
全局性流控
日志统计
防止SQL注入
防止Web攻击
屏蔽工具扫描
黑白名单控制
通过这个功能清单,我们可以发现,流量网关的功能跟Web应用防火墙(WAF)非常类似。WAF一般是基于Nginx/OpenResty的ngx_lua模块开发的Web应用防火墙。一般WAF的代码很简单,专注于使用简单、高性能和轻量级。简单地说就是在Nginx本身的代理能力以外,添加了安全相关功能。用一句话描述其原理,就是解析HTTP请求(协议解析模块),规则检测(规则模块),做不同的防御动作(动作模块),并将防御过程(日志模块)记录下来。
一般的WAF具有如下功能:
防止SQL注入、部分溢出、fuzzing测试、XSS/SSRF等Web攻击
防止Apache Bench之类压力测试工具的攻击
屏蔽常见的扫描黑客工具,比如扫描器
禁止图片附件类目录执行权限、防止webshell上传
支持IP白名单和黑名单功能,直接拒绝黑名单的IP访问
支持URL白名单,定义不需要过滤的URL
支持User-Agent的过滤、支持CC攻击防护、限制单个URL指定时间的访问次数
支持支持Cookie过滤,URL与URL参数过滤
支持日志记录,将所有拒绝的操作记录到日志中
我们定义针对具体的后端业务系统,或者是服务和业务有一定关联性的策略网关,即为业务网关。比如,针对某个系统、某个服务或某个用户分类的流控策略,针对某一类服务的缓存策略,针对某个具体系统的权限验证方式,针对某些用户条件判断的请求过滤,针对具体几个相关API的数据聚合封装,等等。
业务网关一般部署在流量网关之后、业务系统之前,比流量网关更靠近业务系统。我们大部分情况下说的API网关,狭义上指的是业务网关。如果系统的规模不大,我们也会将两者合二为一,使用一个网关来处理所有的工作。
一般来说,API网关有四大职能:
请求接入:作为所有API接口服务请求的接入点,管理所有的接入请求。
业务聚合:作为所有后端业务服务的聚合点,所有的业务服务都可以在这里被调用。
中介策略:实现安全、验证、路由、过滤、流控、缓存等策略,进行一些必要的中介处理。
统一管理:提供配置管理工具,对所有API服务的调用生命周期和相应的中介策略进行统一管理。
API网关的三个关注点:
海量并发的API网关最重要的三个关注点:
(1)保持大规模的inbound请求接入能力(长短连接),比如基于Netty实现。
(2)最大限度地复用outbound的HTTP连接能力,比如基于HttpClient4的异步HttpClient实现。
(3)方便灵活地实现安全、验证、过滤、聚合、限流、监控等各种策略。
目前常见的开源网关大致上按照语言分类有如下几类:
Java:Zuul/Zuul 2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul等;
Nginx+Lua:Open Resty、Kong、Orange、Abtesting Gateway等;
Go:Janus、fagongzi、Grpc-Gateway;
.NET:Ocelot;
Node.js:Express Gateway、MicroGateway。
按照使用范围、成熟度等来划分,主流的有4个:OpenResty、Kong、Zuul/Zuul 2、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实现异步I/O,从而实现了一个简单、比Zuul 1.x更高效的、与Spring Cloud紧密配合的API网关。
Spring Cloud Gateway里明确地区分了Router和Filter,内置了非常多的开箱即用功能,并且都可以通过Spring Boot配置或手工编码链式调用来使用。比如内置了10种Router,直接配置就可以随心所欲地根据Header、Path、Host或Query来做路由。
其核心特性:
开源项目地址:https://github.com/spring-cloud/spring-cloud-gateway/
Zuul是Netflix开源的API网关系统,它的主要设计目标是动态路由、监控、弹性和安全。Zuul的内部原理可以简单看作很多不同功能filter的集合(作为对比,ESB也可以简单被看作管道和过滤器的集合)。这些过滤器(filter)可以使用Groovy或其他基于JVM的脚本编写(当然Java也可以编写),放置在指定的位置,然后可以被Zuul Server轮询,发现变动后动态加载并实时生效。Zuul目前有1.x和2.x两个版本,这两个版本的差别很大。
Zuul 1.x基于同步I/O,也是Spring Cloud全家桶的一部分,可以方便地配合Spring Boot/SpringCloud配置和使用。在Zuul 1.x里,Filter的种类和处理流程如图7-7所示,最主要的就是pre、routing、post这三种过滤器,分别作用于调用业务服务API之前的请求处理、直接响应、调用业务服务API之后的响应处理。
Zuul 2.x最大的改进就是基于Netty Server实现了异步I/O来接入请求,同时基于Netty Client实现了到后端业务服务API的请求。这样就可以实现更高的性能、更低的延迟。此外也调整了Filter类型,将原来的三个核心Filter显式命名为Inbound Filter、Endpoint Filter和Outbound Filter
Zuul 2.x的核心功能:服务发现、负载均衡、连接池、状态分类、重试、请求凭证、HTTP/2、TLS、代理协议、GZip、WebSocket。
项目地址:https://github.com/Netflix/zuul
OpenResty基于Nginx,集成了Lua语言和Lua的各种工具库、可用的第三方模块,这样我们就在Nginx既有的高效HTTP处理的基础上,同时获得了Lua提供的动态扩展能力。因此,我们可以做出各种符合我们需要的网关策略的Lua脚本,以其为基础实现网关系统。
项目地址:http://openresty.org/
Kong基于OpenResty,是一个云原生、快速、可扩展、分布式的微服务抽象层(MicroserviceAbstraction Layer),也叫API网关(API Gateway),在Service Mesh里也叫API中间件(API Middleware)。Kong开源于2015年,核心价值在于其高性能和扩展性。从全球5000强的组织统计数据来看,Kong是现在依然在维护的、在生产环境使用最广泛的网关。
其核心特性:
项目地址:http://openresty.org/
限流功能:
OpenResty/Kong/Zuul 2/SpringCloudGateway性能测试对比:
图中纵坐标轴是QPS,横轴是一个Gateway的数据,每根线是一个场景下的不同网关数据,测试结论如下:
脱离场景谈性能,都是“耍流氓”。同样基准条件下,不同的参数和软件,相对而言的横向比较才有价值。比如同样的机器(比如16GB内存/4核),同样的Server(用Spring Boot,配置路径为api/hello,返回一个helloworld),同样的压测方式和工具(比如用wrk,10个线程,20个并发连接)。我们测试直接访问Server得到的极限QPS(QPS-Direct,29K);配置了一个Spring Cloud Gateway做网关访问的极限QPS(QPS-SCG,11K);同样方式配置一个Zuul 2做网关压测得到的极限QPS(QPS-Zuul2,13K);Kong得到的极限QPS(QPS-Kong,21K);OpenResty得到的极限QPS(QPS-OR,19K)。这个对比就有意义了。
总的来说,Kong的性能非常不错,非常适合做流量网关,并且对于service、route、upstream、consumer、plugins的抽象,也是自研网关值得借鉴的。
但是对于复杂的业务网关不建议使用Kong,Spring Cloud Gateway/Zuul 2对于Java技术栈来说比较方便,可以依赖业务系统的一些通用的类库。Lua不方便,不光是语言的问题,更是复用基础设施的问题。另外,对于网关系统来说,性能不会差一个数量级,问题不大,多加2台机器就可以“搞定”。
从测试的结果来看,如果后端API服务的延迟都较低(例如2ms级别),直连的吞吐量假如是100QPS,Kong可以达到60QPS,OpenResty是50QPS,Zuul 2和Spring CloudGateway大概是35QPS,如果服务本身的延迟(latency)大一点,那么这些差距会逐步缩小。
目前来看Zuul 2的坑还是比较多的: (1)刚出不久,不成熟,没什么文档,还没有太多的实际应用案例。 (2)高并发时出错率较高,1000并发时我们的测试场景有近50%的出错率。 简单使用或轻度定制业务网关系统,目前建议使用Spring CloudGateway作为基础骨架。