本文从百亿流量交易系统微服务网关(API Gateway)的现状和面临的问题出发,阐述微服务架构与 API 网关的关系,理顺流量网关与业务网关的脉络,分享API网关知识与经验。
API网关概述
“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。”
——David Wheeler
分布式服务架构、微服务架构与 API 网关
1. 什么是API网关(API Gateway)
其实,网关跟面向服务架构(Service Oriented Architecture,SOA)和微服务架构(MicroServicesArchitecture,MSA)有很深的渊源。
十多年以前,银行等金融机构完成业务系统大集中以后,分散的系统都变得集中,也带来了各种问题:业务发展过快如何应对,对接系统过多如何集成和管理。为了解决这些问题,业界实现了作用于渠道与业务系统之间的中间层网关,即综合前置系统,由其适配各类渠道和业务,处理各种协议接入、路由与报文转换、同步异步调用等操作,如图7-1所示。
图7-1
人们基于SOA的理念,在综合前置的基础上,进一步增加了服务的元数据管理、注册、中介、编排、治理等功能,逐渐形成了企业服务总线(ESB,EnterpriseService Bus)。例如普元公司推出的PrimetonESB就是一个由本书作者之一参与开发的总线系统,如图7-2所示。
图7-2
面向服务架构(SOA)是一种建设企业IT生态系统的架构指导思想。SOA的关注点是服务,服务基本的业务功能单元由平台中立性的接口契约来定义。通过将业务系统服务化,可以将不同模块解耦,各种异构系统间可以轻松实现服务调用、消息交换和资源共享。不同于以往的孤立业务系统,SOA强调整个企业IT生态环境是一个大的整体。整个IT生态中的所有业务服务构成了企业的核心IT资源。各个系统的业务拆解为不同粒度和层次的模块和服务,服务可以组装到更大的粒度,不同来源的服务可以编排到同一个处理流程中,实现非常复杂的集成场景和更加丰富的业务功能。
SOA从更高的层次对整个企业IT生态进行统一的设计与管理,应用软件被划分为具有不同功能的服务单元,并通过标准的软件接口把这些服务联系起来,以SOA架构实现的企业应用可以更灵活快速地响应企业的业务变化,实现新旧软件资产的整合和复用,降低软件整体拥有成本。
当然基于ESB这种集中式管理的SOA方案也存在种种问题,特别是在面向互联网技术领域的爆发式发展的情况下。
**2. **分布式服务架构、微服务架构与API网关
近年来,随着互联网技术的飞速发展,为了解决以ESB为代表的集中式管理的SOA方案的种种问题,以Apache Dubbo(2011年开源后)与Spring Cloud为代表的分布式服务化技术的出现,给了SOA实现的另外一个选择:去中心化的分布式服务架构(DSA)。分布式服务架构技术不再依赖于具体的服务中心容器技术(比如ESB),而是将服务寻址和调用完全分开,这样就不需要通过容器作为服务代理。
之后又在此基础上随着REST、Docker容器化、领域建模、自动化测试运维等领域的发展,逐渐形成了微服务架构(MSA)。在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立地设计、开发、测试、部署和管理。这时,各个独立部署单元可以选择不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,但是要求必须使用一种语言和平台无关的服务协议作为各个单元之间的通信方式,如图7-3所示。
图7-3
在微服务架构中,由于系统和服务的细分,导致系统结构变得非常复杂,RESTAPI由于其简单、高效、跨平台、易开发、易测试、易集成,成为不二选择。此时一个类似综合前置的系统就产生了,这就是API网关(API Gateway)。API网关作为分散在各个业务系统微服务的API聚合点和统一接入点,外部请求通过访问这个接入点,即可访问内部所有的REST API服务。
跟SOA/ESB类似,企业内部向外暴露的所有业务服务能力,都可以通过API网关上管理的API服务得以体现,所以API网关上也就聚合了企业所有直接对外提供的IT业务能力。
3. API网关的技术趋势
Spring Cloud和SOA非常火,MSA、gRPC、Gateway都有着非常高的关注度,通过GitHub的搜索来看,Gateway类型的项目也非常热门。
从https://github.com/search?o=desc&p=1&q=gateway&s=stars&type=Repositories上可以看到,前10页的100个项目,使用Go语言实现的Gateway差不多占一半,从语言分类上来看:Go>Node.js/JavaScript>Java>Lua>C/C++>PHP>Python/Ruby/Perl。
API网关的定义、职能与关注点
1. API网关的定义
网关的角色是作为一个API架构,用来保护、增强和控制对于API服务的访问(The role of a Gateway in anAPI architecture is to protect, enrich and control access to API services.)。
引用自https://github.com/strongloop/microgateway。
API网关是一个处于应用程序或服务(提供REST API接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样REST API接口服务就被API网关保护起来,对所有的调用者透明。因此,隐藏在API网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
这样,网关系统就可以代理业务系统的业务服务API。此时网关接收外部其他系统的服务调用请求,也需要访问后端的实际业务服务。在接收请求的同时,可以实现安全相关的系统保护措施。在访问后端业务服务的时候,可以根据相关的请求信息做出判断,路由到特定的业务服务上,或者调用多个服务后聚合成新的数据返回给调用方。网关系统也可以把请求的数据做一些过滤和预处理,同理也可以把返回给调用者的数据做一些过滤和预处理,即根据需要对请求头/响应头、请求报文/响应报文做一些修改。如果不做这些额外的处理,则简单直接代理服务API功能,我们称之为透传。
同时,由于REST API的语言无关性,基于API网关,后端服务可以是任何异构系统,不论Java、.NET、Python,还是PHP、ROR、Node.js等,只要支持REST API,就可以被API网关管理起来。
2. API网关的职能
API网关的职能如图7-4所示。
图7-4
一般来说,API网关有四大职能。
- 请求接入:作为所有API接口服务请求的接入点,管理所有的接入请求。
- 业务聚合:作为所有后端业务服务的聚合点,所有的业务服务都可以在这里被调用。
- 中介策略:实现安全、验证、路由、过滤、流控、缓存等策略,进行一些必要的中介处理。
- 统一管理:提供配置管理工具,对所有API服务的调用生命周期和相应的中介策略进行统一管理。
3. API网关的关注点
API网关并不是一个典型的业务系统,而是一个为了让业务系统更专注于业务服务本身,给API服务提供更多附加能力的一个中间层。
在设计和实现API网关时,需要考虑两个目标:
(1)开发维护简单,节约人力成本和维护成本。即应选择成熟的简单可维护的技术体系。
(2)高性能,节约设备成本,提高系统吞吐能力。要求我们需要针对API网关的特点进行一些特定的设计和权衡。
当并发量小的时候,这些都不是问题。一旦系统的API访问量非常大,这些都会成为关键的问题。
海量并发的API网关重要的三个关注点:
(1)保持大规模的inbound请求接入能力(长短连接),比如基于Netty实现。
(2)大限度地复用outbound的HTTP连接能力,比如基于HttpClient4的异步HttpClient实现。
(3)方便灵活地实现安全、验证、过滤、聚合、限流、监控等各种策略。
API网关的分类与技术分析
1. API网关的分类
如果对上述的目标和关注点进行更深入的思考,那么所有需要考虑的问题和功能可以分为两类。
- 一类是全局性的,跟具体的后端业务系统和服务完全无关的部分,比如安全策略、全局性流控策略、流量分发策略等。
- 一类是针对具体的后端业务系统,或者是服务和业务有一定关联性的部分,并且一般被直接部署在业务服务的前面。
随着互联网的复杂业务系统的发展,这两类功能集合逐渐形成了现在常见的两种网关系统:流量网关和业务网关,如图7-5所示。
图7-5
2. 流量网关与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参数过滤;
- 支持日志记录,将所有拒绝的操作记录到日志中。
以上WAF的内容主要参考如下两个项目:
流量网关的开源实例还可以参考的开源项目Kong(基于OpenResty)。
3. 业务网关
我们定义针对具体的后端业务系统,或者是服务和业务有一定关联性的策略网关,即为业务网关。比如,针对某个系统、某个服务或某个用户分类的流控策略,针对某一类服务的缓存策略,针对某个具体系统的权限验证方式,针对某些用户条件判断的请求过滤,针对具体几个相关API的数据聚合封装,等等。
业务网关一般部署在流量网关之后、业务系统之前,比流量网关更靠近业务系统。我们大部分情况下说的API网关,狭义上指的是业务网关。如果系统的规模不大,我们也会将两者合二为一,使用一个网关来处理所有的工作。
开源****网关的分析与调研
常见的开源网关介绍
常见的开源网关如图7-6所示。
图7-6
目前常见的开源网关大致上按照语言分类有如下几类。
- Nginx+Lua:Open Resty、Kong、Orange、Abtesting Gateway等;
- Java:Zuul/Zuul 2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul等;
- Go:Janus、fagongzi、Grpc-Gateway;
- .NET:Ocelot;
- Node.js:Express Gateway、MicroGateway。
按照使用范围、成熟度等来划分,主流的有4个:OpenResty、Kong、Zuul/Zuul 2、Spring Cloud Gateway,此外fagongzi API网关近也获得不少关注。
1. Nginx+Lua网关
OpenResty
OpenResty基于Nginx,集成了Lua语言和Lua的各种工具库、可用的第三方模块,这样我们就在Nginx既有的高效HTTP处理的基础上,同时获得了Lua提供的动态扩展能力。因此,我们可以做出各种符合我们需要的网关策略的Lua脚本,以其为基础实现网关系统。
Kong
项目地址:https://konghq.com/与https://github.com/kong/kong
Kong基于OpenResty,是一个云原生、快速、可扩展、分布式的微服务抽象层(MicroserviceAbstraction Layer),也叫API网关(API Gateway),在Service Mesh里也叫API中间件(API Middleware)。
Kong开源于2015年,核心价值在于其高性能和扩展性。从全球5000强的组织统计数据来看,Kong是现在依然在维护的、在生产环境使用广泛的网关。
核心优势如下。
- 可扩展:可以方便地通过添加节点实现水平扩展,这意味着可以在很低的延迟下支持很大的系统负载。
- 模块化:可以通过添加新的插件来扩展Kong的能力,这些插件可以通过RESTful Admin API来安装和配置。
- 在任何基础架构上运行:Kong在任何地方都能运行,比如在云或混合环境中部署Kong,或者单个/全球的数据中心。
ABTestingGateway
项目地址:https://github.com/CNSRE/ABTestingGateway
ABTestingGateway是一个可以动态设置分流策略的网关,关注与灰度发布相关的领域,基于Nginx和ngx-lua开发,使用Redis作为分流策略数据库,可以实现动态调度功能。
ABTestingGateway是新浪微博内部的动态路由系统dygateway的一部分,目前已经开源。在以往的基于Nginx实现的灰度系统中,分流逻辑往往通过rewrite阶段的if和rewrite指令等实现,优点是性能较高,缺点是功能受限、容易出错,以及转发规则固定,只能静态分流。ABTestingGateway则采用 ngx-lua,通过启用lua-shared-dict和lua-resty-lock作为系统缓存和缓存锁,系统获得了较为接近原生Nginx转发的性能。
功能特性如下。
- 支持多种分流方式,目前包括iprange、uidrange、uid尾数和指定uid分流;
- 支持多级分流,动态设置分流策略,即时生效,无须重启;
- 可扩展性,提供了开发框架,开发者可以灵活添加新的分流方式,实现二次开发;
- 高性能,压测数据接近原生Nginx转发;
- 灰度系统配置写在Nginx配置文件中,方便管理员配置;
- 适用于多种场景:灰度发布、AB测试和负载均衡等。
据了解,美团网内部的Oceanus也是基于Nginx和ngx-lua扩展实现的,主要提供服务注册与发现、动态负载均衡、可视化管理、定制化路由、安全反扒、Session ID复用、熔断降级、一键截流和性能统计等功能。
2. 基于Java语言的网关
Zuul/Zuul2
项目地址:https://github.com/Netflix/zuul
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,如图7-8所示。
图7-7
图7-8
Zuul 2.x的核心功能:服务发现、负载均衡、连接池、状态分类、重试、请求凭证、HTTP/2、TLS、代理协议、GZip、WebSocket。
SpringCloud 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实现异步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集成;
- 非常方便地实现断言和过滤器;
- 请求限流;
- 路径重写。
graviteeGateway
项目地址:https://gravitee.io/与https://github.com/gravitee-io/gravitee-gateway
KaazingWebSocket Gateway
项目地址:
https://github.com/kaazing/gateway与https://kaazing.com/products/websocket-gateway/
Kaazing WebSocket Gateway是一个专门针对和处理WebSocket的网关,宣称提供世界的企业级WebSocket服务能力。具体如下特性:
- 标准WebSocket支持,支持全双工的双向数据投递;
- 线性扩展,无状态架构意味着可以部署更多机器来扩展服务能力;
- 验证,鉴权,单点登录支持,跨域访问控制;
- SSL/TLS加密支持;
- WebSocket keepalive和TCP半开半关探测;
- 通过负载均衡和集群实现高可用;
- Docker支持;
- JMS/AMQP等支持;
- IP白名单;
- 自动重连和消息可靠接受保证;
- Fanout处理策略;
- 实时缓存等。
Dromara soul
项目地址:https://github.com/Dromara/soul。
Soul是一个异步的、高性能的、跨语言的、响应式的API网关,提供了统一的HTTP访问。
- 支持各种语言,无缝集成Dubbo和SpringCloud;
- 丰富的插件支持鉴权、限流、熔断、防火墙等;
- 网关多种规则动态配置,支持各种策略配置;
- 插件热插拔,易扩展;
- 支持集群部署,支持A/B Test。
3. 基于Go语言的网关
fagongzi
项目地址:https://github.com/fagongzi/gateway
fagongzi Gateway是一个Go实现的功能全面的API网关,自带了一个Rails实现的Web UI管理界面。
功能特性:流量控制、熔断、负载均衡、服务发现、插件机制、路由(分流,复制流量)、API聚合、API参数校验、API访问控制(黑白名单)、API默认返回值、API定制返回值、API结果Cache、JWT认证、API Metric导入Prometheus、API失败重试、后端Server的健康检查、开放管理API(gRPC、RESTful)、支持WebSocket协议。
Janus
项目地址:https://github.com/hellofresh/janus
Janus是一个轻量级的API网关和管理平台,能实现控制谁、什么时候、如何访问这些REST API,同时它也记录了所有的访问交互细节和错误。使用Go实现API网关的一个好处在于,一般只需要一个单独的二进制文件即可运行,没有复杂的依赖关系。功能特性:
- 热加载配置,不需要重启网关进程;
- HTTP连接的优雅关闭;
- 支持OpenTracing,从而可以进行分布式跟踪;
- 支持HTTP/2;
- 可以针对每一个API实现断路器;
- 重试机制;
- 流控,可以针对每一个用户或key;
- CORS过滤,可以针对具体的API;
- 多种开箱即用的验证协议支持,比如JWT、OAuth 2.0和Basic Auth;
- Docker Image支持。
4. .NET
Ocelot
项目地址:https://github.com/ThreeMammals/Ocelot
功能特性:路由、请求聚合、服务发现(基于Consul或Eureka)、服务Fabric、WebSockets、验证与鉴权、流控、缓存、重试策略与QoS、负载均衡、日志与跟踪、请求头、Query字符串转换、自定义的中间处理、配置和管理REST API。
5. Node.js
Express Gateway
项目地址:
https://github.com/ExpressGateway/express-gateway与https://www.express-gateway.io/
Express Gateway是一个基于Node.js开发,使用Express和Express中间件实现的REST API网关。
功能特性:
- 动态中心化配置;
- API消费者和凭证管理;
- 插件机制;
- 分布式数据存储;
- 命令行工具CLI。
MicroGateway
项目地址:
https://github.com/strongloop/microgateway与https://developer.ibm.com/apiconnect
StrongLoop是IBM的一个子公司,MicroGateway网关基于Node.js/Express和Nginx构建,作为IBM API Connect,同时也是IBM云生态的一部分。MicroGateway是一个聚焦于开发者,可扩展的网关框架,它可以增强我们对微服务和API的访问能力。
核心特性:
- 安全和控制,基于Swagger(OpenAPI)规范;
- 内置了多种网关策略,API Key验证、流控、OAuth 2.0、JavaScript脚本支持;
- 使用Swagger扩展(API Assembly)实现网关策略(安全、路由、集成等);
- 方便地自定义网关策略。
此外,MicroGateway还有几个特性:
- 通过集成Swagger,实现基于Swagger API定义的验证能力;
- 使用datastore来保持需要处理的API数据模型;
- 使用一个流式引擎来处理多种策略,使API设计者可以更好地控制API的生命周期。
核心架构如图7-9所示。
图7-9
四大开源网关的对比分析
1. OpenResty/Kong/Zuul 2/SpringCloud Gateway重要特性对比
各项指标对比如表7-1所示。
以限流功能为例:
- Spring Cloud Gateway目前提供了基于Redis的Ratelimiter实现,使用的算法是令牌桶算法,通过YAML文件进行配置;
- Zuul2可以通过配置文件配置集群限流和单服务器限流,也可通过Filter实现限流扩展;
- OpenResty可以使用resty.limit.count、resty.limit.conn、resty.limit.req来实现限流功能,可实现漏桶或令牌通算法;
- Kong拥有基础限流组件,可在基础组件源代码基础上进行Lua开发。
对Zuul/Zuul 2/Spring Cloud Gateway的一些功能点分析可以参考Spring Cloud Gateway作者Spencer Gibb的文章:https://spencergibb.netlify.com/preso/detroit-cf-api-gateway-2017-03/。
2. OpenResty/Kong/Zuul 2/SpringCloudGatew****ay性能测试对比
分别使用3台4Core、16GB内存的机器,作为API服务提供者、Gateway、压力机,使用wrk作为性能测试工具,对OpenResty/Kong/Zuul 2/SpringCloud Gateway进行简单小报文下的性能测试,如图7-10所示。
图7-10
图中纵坐标轴是QPS,横轴是一个Gateway的数据,每根线是一个场景下的不同网关数据,测试结论如下:
- 实测情况是性能SCG~Zuul 2 << OpenResty~< Kong << Direct(直连);
- Spring Cloud Gateway、Zuul 2的性能差不多,大概是直连的40%;
- OpenResty、Kong的性能差不多,大概是直连的60%~70%;
- 大并发下,例如模拟200并发用户、1000并发用户时,Zuul 2会有很大概率返回出错。
开源网关的技术总结
1. 开源网关的测试分析
脱离场景谈性能,都是“耍流氓”。性能就像温度,不同的场合下标准是不一样的。同样是18摄氏度,老人觉得冷,年轻人觉得合适,企鹅觉得热,冰箱里的蔬菜可能容易坏了。
同样基准条件下,不同的参数和软件,相对而言的横向比较才有价值。比如同样的机器(比如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,或者更明确地说是不建议在Java技术栈的系统深度定制Kong或OpenResty,主要是出于工程性方面的考虑。举个例子:假如我们有多个不同业务线,鉴权方式五花八门,都是与业务多少有点相关的。这时如果把鉴权在网关实现,就需要维护大量的Lua脚本,引入一个新的复杂技术栈是一个成本不低的事情。
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作为基础骨架。
2. 各类网关的Demo与测试
以上测试用到的模拟服务和网关Demo代码,大部分可以在这里找到:
https://github.com/ kimmking/atlantis。
我们使用Vert.x实现了一个简单网关,性能跟Zuul 2和Spring Cloud Gateway差不多。另外也简单模拟了一个Node.js做的网关Demo,加了keep-alive和pool,Demo的性能测试结果大概是直连的1/9,也就是Spring Cloud Gateway或Zuul 2的1/4左右。
百亿流量交易系统API网关设计
百亿流量交易系统API网关的现状和面临问题
1. 百亿流量系统面对的业务现状
百亿流量系统面对的业务现状如图7-11所示。
图7-11
我们目前面临的现状是日常十几万的并发在线长连接数(不算短连接),每天长连接总数为3000万+,每天API的调用次数超过100亿次,每天交易订单数为1.5亿个。
在这种情况下,API网关设计的一个重要目标就是:如何借助API网关为各类客户提供精准、专业、个性化的服务,保障客户实时地获得业务系统的数据和业务能力。
2. 网关系统与其他系统的关系
某交易系统的API网关系统与其他系统的关系大致如图7-12所示。
图7-12
3. 网关系统典型的应用场景
我们的API网关系统为Web端、移动App端客户提供服务,也为大量API客户提供API调用服务,同时支持REST API和WebSocket协议。
作为实时交易系统的前置系统,必须精准及时为客户提供新的行情和交易信息。一旦出现数据的延迟或错误,都会给客户造成无法挽回的损失。
另外针对不同的客户和渠道,网关系统需要提供不同的安全、验证、流控、缓存策略,同时可以随时聚合不同视角的数据进行预处理,保障系统的稳定可靠和数据的实时。
4. 交易系统API的特点
作为一个全球性的交易系统,我们的API特点总结如下。
- 访问非常集中:核心的一组API占据了访问量的一半以上;
- 访问非常频繁:QPS非常高,日均访问量非常大;
- 数据格式固定:交易系统处理的数据格式非常固定;
- 报文数据量小:每次请求传输的数据一般不超过10KB;
- 用户全世界分布:客户分布在全世界的各个国家;
- 分内部调用和外部调用:除了API客户直接调用的API,其他的API都是由内部其他系统调用的;
- 7×24小时不间断服务:系统需要提供高可用、不间断的服务能力,以满足不同时区客户的交易和自动化策略交易;
- 外部用户有一定技术能力:外部API客户,一般是集成我们的API,实现自己的交易系统。
5. 交易系统API网关面临的问题
问题1:流量不断增加。
如何合理控制流量,如何应对突发流量,如何大限度地保障系统稳定,都是重要的问题。特别是网关作为一个直接面对客户的系统,出现的任何问题都会放大百倍。很多千奇百怪的从来没人遇到的问题随时都可能出现。
问题2:网关系统越来越复杂。
现有的业务网关经过多年发展,里面有大量的业务嵌入,并且存在多个不同的业务网关,相互之间没有任何关系,也没有沉淀出基础设施。
同时技术债务太多,系统里硬编码实现了全局性网关策略及很多业务规则,导致维护成本较大。
问题3:API网关管理比较困难。
海量并发下API的监控指标设计和数据的收集也是一个不小的问题。7×24小时运行的技术支持也导致维护成本较高。
问题4:选择推送还是拉取。
使用短连接还是长连接,REST API还是WebSocket?业务渠道较多(多个不同产品线的Web、App、API等形成十几个不同的渠道),导致用户的使用行为难以控制。
业务网关的设计与佳实践
1. API网关1.0
我们的API网关1.0版本是多年前开发的,是直接使用OpenResty定制的,全局的安全测试、流量的路由转发策略、针对不同级别的限流等都是直接用Lua脚本实现。
这样就导致在经历了业务飞速发展以后,系统里存在非常多的相同功能或不同功能的Lua脚本,每次上线或维护都需要找到受影响的其中几个或几十个Lua脚本,进行策略调整,非常不方便,策略控制的粒度也不够细。
2. API网关2.0
在区分了流量网关和业务网关以后,2017年开始实现了流量网关和业务网关的分离,流量网关继续使用OpenResty定制,只保留少量全局性、不经常改动的配置功能和对应的Lua脚本。
业务网关使用Vert.x实现的Java系统,部署在流量网关和后端业务服务系统之间,利用Vert.x的响应式编程能力和异步非阻塞I/O能力、分布式部署的扩展能力,初步解决了问题1和问题2,如图7-13所示。
图7-13
Vert.x是一个基于事件驱动和异步非阻塞I/O、运行于JVM上的框架,如图7-14所示。在Vert.x里,Verticle是基础的开发和部署单元,不同的Vert.x可以通过Event Bus传递数据,进而方便地实现高并发性能的网络程序。关于Vert.x原理的分析可以参考阿里架构师宿何的blog:
https://www.sczyh30.com/tags/Vert-x/。
图7-14
Vert.x同时很好地支持了WebSocket协议,所以可以方便地实现支持REST API和WebSocket、完全异步的网关系统,如图7-15所示。
图7-15
一个高性能的API网关系统,缓存是必不可少的部分。无论分发冷热数据,降低对业务系统的压力,还是作为中间数据源,为服务聚合提供高效可复用的业务数据,缓存都发挥了巨大作用。
3. API网关的日常监控
我们使用多种工具对API进行监控和管理,包括全链路访问跟踪、连接数统计分析、全世界重要国家和城市的波测访问统计。网关技术团队每时每刻都关注着数据的变化趋势。各个业务系统研发团队每天安排专人关注自己系统的API性能(吞吐量和延迟),推进性能问题解决和持续优化。这就初步解决了问题3。
4. 推荐外部客户使用WebSocket和API SDK
由于外部客户需要自己通过API网关调用API服务来集成业务服务能力到自己的系统。各个客户的技术能力和系统处理能力有较大差异,使用行为也不同。对于不断发展变动的交易业务数据,客户调用API频率太低会影响数据实时性,调用频率太高则可能会浪费双方的系统资源。同时利用WebSocket的消息推送特点,我们可以在网关系统控制客户接收消息的频率、单个用户的连接数量等,随时根据业务系统的情况动态进行策略调整。综合考虑,WebSocket是一个比REST API更加实时可靠、更加易于管理的方式。另外对于习惯使用REST API的客户,我们也通过将各种常见使用场景封装成多种不同语言的API SDK(包括Java/C++/C#/Python),进而统一用户的API调用方式和行为。在研发、产品、运营各方的配合下,逐步协助客户使用WebSocket协议和API SDK,基本解决了问题4。
5. API网关的性能优化
API网关系统作为API服务的统一接入点,为了给用户提供优质的用户体验,必须长期做性能优化工作。不仅API网关自己做优化,同时可以根据监控情况,时刻发现各业务系统的API服务能力,以此为出发点,推动各个业务系统不断优化API性能。
举一个具体的例子,某个网关系统连接经常强烈抖动(如图7-16所示),严重影响系统的稳定性、浪费系统资源,经过排除发现:
(1)有爬虫IP不断爬取我们的交易数据,而且这些IP所在网段都没有在平台产生任何实际交易,高单爬虫IP的每日新建连接近100万次,平均每秒十几次。
(2)有部分API客户的程序存在bug,而且处理速度有限,不断地重复“断开并重新连接”,再尝试重新对API数据进行处理,严重影响了客户的用户体验。
针对如上分析,我们采取了如下处理方式:
(1)对于每天认定的爬虫IP,加入黑名单,直接在流量网关限制其访问我们的API网关。
(2)对于存在bug的API客户,协助对方进行问题定位和bug修复,增强客户使用信心。
(3)对于处理速度和技术能力有限的客户,基于定制的WebSocket服务,使用滑动时间窗口算法,在业务数据变化非常大时,对分发的消息进行批量优化。
图7-16
(4)对于未登录和识别身份的API调用,流量网关实现全局的流控策略,增加缓存时间和限制调用次数,保障系统稳定。
(5)业务网关根据API服务的重要等级和客户的分类,进一步细化和实时控制网关策略,大限度地保障核心业务和客户的使用。
从监控图表可以看到,优化之后的效果非常明显,系统稳定,连接数平稳。