百亿流量API网关的设计记录


前言

本次分享我们从第七届MAD技术论坛-《API网关的设计与实现记录》API 网关(API Gateway)的现状和面临问题出发,阐述微服务架构与 API 网关的关系,理顺流量网关与业务网关的脉络。
原视频地址:第七届MAD技术论坛——《百亿流量API网关设计与实现》


一、现状与问题

1.API网关概述

计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。 —— David Wheeler

1.1 什么是API网关(API Gateway)-历史脉络

API Gateway与综合前置、ESB
其实网关跟面向服务架构(Service Oriented Architecture,SOA)和微服务架构(MicroServices Architecture,MSA)有很深的渊源。

十多年以前,银行等金融机构完成全国业务系统大集中以后,分散的系统都变得集中,同时也带来各种问题:业务发展过快如何应对,对接系统过多如何集成和管理。为了解决这些问题,业界实现了作用于渠道与业务系统之间的中间层网关,即综合前置系统,由其适配各类渠道和业务,处理各种协议接入、路由与报文转换、同步异步调用等。
在这里插入图片描述

人们基于SOA的理念,在综合前置的基础上,进一步增加了服务的元数据管理、注册、中介、编排、治理等功能,逐渐形成了企业服务总线(ESB,EnterpriseService Bus)。

在这里插入图片描述

在这里插入图片描述
PS:当然基于 ESB 这种集中式管理的 SOA 方案也存在着种种问题,特别是面向互联网技术领域的爆发式发展的情况下。

1.2 API的使用特点和系统面临的问题

1.2.1 使用特点

  • 访问非常集中
  • 用户全世界分布
  • 数据格式固定
  • 分内部调用和外部调用
  • 报文数据量小
  • 7x24小时提供服务
  • 访问非常频繁
  • 外部用户有一定技术能力

API网关面对大流量访问一定需要对其分级,区分内部访问还是外部方位,以及每个系统访问的限流。

1.2.2面临的问题

  • 流量的不断增加
    如何合理控制流量
    如何应对突发流量
    直接面对用户,任何问题都会放大百倍
  • 网关系统太复杂
    大量的业务嵌入
    多个系统并存
    硬编码业务规则多,维护成本较大
  • 管理比较困难
    监控指数度量
    收集数据量太大
    7x24小时运行,维护成本较高
  • 推送还是拉取
    短连接还是长连接
    REST API还是websocket
    渠道较多(Web、app、api等十几个),难以控制
    在这里插入图片描述

2. 调研与分析(开源网关)

2.1 网关与微服务

近年来,随着互联网技术的飞速发展,为了解决以ESB为代表的集中式管理的SOA方案的种种问题,以Apache Dubbo(2011年开源后)与Spring Cloud为代表的分布式服务化技术的出现,给了SOA实现的另外一个选择:去中心化的分布式服务架构(DSA)。分布式服务架构技术不再依赖于具体的服务中心容器技术(比如ESB),而是将服务寻址和调用完全分开,这样就不需要通过容器作为服务代理。

之后又在此基础上随着REST、Docker容器化、领域建模、自动化测试运维等领域的发展,逐渐形成了微服务架构(MSA)。在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立地设计、开发、测试、部署和管理。这时,各个独立部署单元可以选择不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,但是要求必须使用一种语言和平台无关的服务协议作为各个单元之间的通信方式。
在这里插入图片描述
在微服务架构中,由于系统和服务的细分,导致系统结构变得非常复杂,RESTAPI由于其简单、高效、跨平台、易开发、易测试、易集成,成为不二选择。此时一个类似综合前置的系统就产生了,这就是API网关(API Gateway)。API网关作为分散在各个业务系统微服务的API聚合点和统一接入点,外部请求通过访问这个接入点,即可访问内部所有的REST API服务。

2.2 网关分类与功能

将所有需要考虑的问题和功能可以分为两类:

  • 全局性的,跟具体的后端业务系统和服务完全无关的部分,比如安全策略、全局性流控策略、流量分发策略等。

  • 针对具体的后端业务系统,或者是服务和业务有一定关联性的部分,并且一般被直接部署在业务服务的前面。

随着互联网的复杂业务系统的发展,这两类功能集合逐渐形成了现在常见的两种网关系统:流量网关和业务网关
在这里插入图片描述

2.2.1 流量网关与WAF

我们定义全局性的、跟具体的后端业务系统和服务完全无关的策略网关,即为流量网关。这样流量网关关注全局流量的稳定与安全,比如防止各类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参数过滤

  • 支持日志记录,将所有拒绝的操作记录到日志中

2.2.2 业务网关

我们定义针对具体的后端业务系统,或者是服务和业务有一定关联性的策略网关,即为业务网关。比如,针对某个系统、某个服务或某个用户分类的流控策略,针对某一类服务的缓存策略,针对某个具体系统的权限验证方式,针对某些用户条件判断的请求过滤,针对具体几个相关API的数据聚合封装,等等。
业务网关一般部署在流量网关之后、业务系统之前,比流量网关更靠近业务系统。我们大部分情况下说的API网关,狭义上指的是业务网关。如果系统的规模不大,我们也会将两者合二为一,使用一个网关来处理所有的工作。

2.3 API网关的四大职能和三个关注点

一般来说,API网关有四大职能

  • 请求接入:作为所有API接口服务请求的接入点,管理所有的接入请求。

  • 业务聚合:作为所有后端业务服务的聚合点,所有的业务服务都可以在这里被调用。

  • 中介策略:实现安全、验证、路由、过滤、流控、缓存等策略,进行一些必要的中介处理。

  • 统一管理:提供配置管理工具,对所有API服务的调用生命周期和相应的中介策略进行统一管理。
    在这里插入图片描述
    API网关的三个关注点:
    海量并发的API网关最重要的三个关注点:
    (1)保持大规模的inbound请求接入能力(长短连接),比如基于Netty实现。
    (2)最大限度地复用outbound的HTTP连接能力,比如基于HttpClient4的异步HttpClient实现。
    (3)方便灵活地实现安全、验证、过滤、聚合、限流、监控等各种策略。

在这里插入图片描述

2.4 常见的开源网关

在这里插入图片描述
目前常见的开源网关大致上按照语言分类有如下几类:

  • 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。

2.4.1主流开源网关的对比分析

在这里插入图片描述

2.4.1.1 SpringCloud 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来做路由。
其核心特性

  • 通过请求参数匹配路由;
  • 通过断言和过滤器实现路由;
  • 与Hystrix熔断集成;
  • 与Spring Cloud DiscoveryClient集成;
  • 非常方便地实现断言和过滤器;
  • 请求限流;
  • 路径重写。

开源项目地址:https://github.com/spring-cloud/spring-cloud-gateway/

2.4.1.2 Zuul/Zuul2

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

2.4.1.3 OpenResty

OpenResty基于Nginx,集成了Lua语言和Lua的各种工具库、可用的第三方模块,这样我们就在Nginx既有的高效HTTP处理的基础上,同时获得了Lua提供的动态扩展能力。因此,我们可以做出各种符合我们需要的网关策略的Lua脚本,以其为基础实现网关系统。

项目地址:http://openresty.org/

2.4.1.3 Kong

Kong基于OpenResty,是一个云原生、快速、可扩展、分布式的微服务抽象层(MicroserviceAbstraction Layer),也叫API网关(API Gateway),在Service Mesh里也叫API中间件(API Middleware)。Kong开源于2015年,核心价值在于其高性能和扩展性。从全球5000强的组织统计数据来看,Kong是现在依然在维护的、在生产环境使用最广泛的网关。

其核心特性

  • 可扩展:可以方便地通过添加节点实现水平扩展,这意味着可以在很低的延迟下支持很大的系统负载。
  • 模块化:可以通过添加新的插件来扩展Kong的能力,这些插件可以通过RESTful Admin API来安装和配置。
  • 在任何基础架构上运行:Kong在任何地方都能运行,比如在云或混合环境中部署Kong,或者单个/全球的数据中心。

项目地址:http://openresty.org/

2.4.1.4 对比分析

限流功能

  • Spring Cloud Gateway目前提供了基于Redis的Ratelimiter实现,使用的算法是令牌桶算法,通过YAML文件进行配置;
  • Zuul2可以通过配置文件配置集群限流和单服务器限流,也可通过Filter实现限流扩展;
  • OpenResty可以使用resty.limit.count、resty.limit.conn、resty.limit.req来实现限流功能,可实现漏桶或令牌通算法;
  • Kong拥有基础限流组件,可在基础组件源代码基础上进行Lua开发。

OpenResty/Kong/Zuul 2/SpringCloudGateway性能测试对比:
在这里插入图片描述
图中纵坐标轴是QPS,横轴是一个Gateway的数据,每根线是一个场景下的不同网关数据,测试结论如下:

  • 实测情况是性能SCG~Zuul 2 << OpenResty~< Kong << Direct(直连);
  • Spring Cloud Gateway、Zuul 2的性能差不多,大概是直连的40%;
  • OpenResty、Kong的性能差不多,大概是直连的60%~70%;
  • 大并发下,例如模拟200并发用户、1000并发用户时,Zuul 2会有很大概率返回出错。
2.4.1.5 开源网关的技术总结

脱离场景谈性能,都是“耍流氓”。同样基准条件下,不同的参数和软件,相对而言的横向比较才有价值。比如同样的机器(比如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作为基础骨架。