本文从百亿流量交易系统微服务网关(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 类型的项目也非常热门。
从这里可以看到,前 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 的文章。
2. OpenResty/Kong/Zuul 2/SpringCloudGateway 性能测试对比
分别使用 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 服务的重要等级和客户的分类,进一步细化和实时控制网关策略,最大限度地保障核心业务和客户的使用。
从监控图表可以看到,优化之后的效果非常明显,系统稳定,连接数平稳。
本文内容来自作者图书作品《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud 和 Service Mesh》,点击购买。
作者简介:
程超,网名小程故事多,现任某公司高级架构师,超过 12 年的 Java 研发经验,8 年技术管理和架构经验,熟悉支付和电商领域,擅长微服务生态建设和运维监控,对 Dubbo、Spring Cloud 和 gRPC 等微服务框架有深入研究,帮助多家公司进行过微服务建设和改造。合著作品《深入分布式缓存》,阿里云 MVP、云栖社区外部专家、Codingfly 社区特聘技术专家、CSDN 博主专家。
梁桂钊,现任某互联网公司高级开发工程师,参与过内容分发、K12 教育、淘系电商等项目。目前,专注于新零售电商服务的业务摸索和电商服务创新实践。具有 Java 核心技术、微服务、分布式、高并发等领域一线实战经验,并对新兴技术方向和各种开源框架有浓厚兴趣。公众号「服务端思维」的作者。
秦金卫(KimmKing),现任某公司高级技术总监 /Apache Dubbo PPMC,阿里前架构师 / 某商业银行北京研发中心负责人。关注互联网、电商、金融、支付、区块链等领域,10 多年研发管理和架构经验,对于中间件、SOA、微服务,以及各种开源技术非常热衷,活跃于 Dubbo、Fastjson、Mule、ActiveMQ 等多个开源社区。个人博客 http://kimmking.github.io。
方志斌,现任某物联网公司高级研发工程师。目前专注于大型物联网平台架构的设计与开发工作。对于微服务、分布式、集群有一定的研究和实战经验。对 Java 领域的开源框架有浓厚的兴趣,喜欢深入分析、总结框架源码。SpringForAll 社区核心成员,组织多次社区技术专题、问答等活动。
张逸,架构编码实践者,微服务架构设计者,领域驱动设计布道师,大数据平台架构师。著译作包括《软件设计精要与模式》《恰如其分的软件架构》《人件》等。个人微信公众号为「逸言」,个人博客:http://zhangyi.xyz。
杜琪,网名阿杜,现任蚂蚁金服高级研发工程师,2015 年 6 月毕业于南开大学,计算机系统结构硕士。毕业后开始接触分布式业务系统开发,曾在有赞负责用户中心基础服务,对分布式业务系统的稳定性、可靠性有丰富的经验。喜欢研究底层技术,喜欢研究疑难技术问题,例如 JVM 内存问题排查、GC 调优,等等。有对外输出分享的习惯,是公众号 javaadu 的维护者。
殷琦,网名涤生,现任“美团点评”技术专家,2015 年 3 月毕业于东华大学,软件工程硕士。2015 年 3 月加入“美团点评”基础架构部,开始接触微服务架构,之后一直从事服务框架的研发工作,对微服务架构发展与演进有非常深刻的认识。个人比较喜欢研究并分享新技术,时刻关注并实践微服务架构前沿的技术,如 Service Mesh、Serverless 等。
肖冠宇,曾就职于小米、人民网等互联网公司,具有丰富的大数据一线实战经验,专注大数据处理技术及机器学习算法研究。著有《企业大数据处理:Spark、Druid、Flume 与 Kafka 应用实践》《Python3 快速入门与实战》等书籍。
评论 3 条评论