浅析API统一网关(API-GATEWAY)的设计与实现

发布于:2024-04-29 ⋅ 阅读:(24) ⋅ 点赞:(0)

一,API统一网关的定义

API网关是随着微服务概念兴起的一种架构模式。原本一个庞大的单体应用业务系统被拆分成许多微服务系统进行独立的维护和部署,服务拆分带来的变化是API的规模成倍增长,API的管理难度也在日益增加,使用API网关发布和管理API逐渐成为一种趋势。一般来说,API网关是运行于外部请求与内部服务之间的一个流量入口,实现对外部请求的协议转换、鉴权、流控、参数校验、监控等通用功能。

iShot2024-03-29 15.10.23.png 对于API网关来说,功能可抽象为两点,一是路由转发,二是过滤器,这里的过滤指的便是流量的过滤

 

二,API统一网关的意义

一般而言,没有api-gateway,公司或多或少都会出现这样的情况弊

  • 客户端到微服务直接通信,强耦合;
  • 需要多次请求,客户端聚合数据,工作量巨大,延迟高;
  •  协议不利于统一,各个部门间有差异,需要端来兼容;
  •  面向端的 API 适配,耦合到了内部服务;(如多包网关统一了IOS数据的返回)
  •  多终端兼容逻辑复杂,每个服务都需要处理;
  •  统一逻辑无法收敛,比如安全认证、限流

举我们之前内部的服务为例,体现的弊端更是有以下几点

(1)我们的流量往往是通过域名请求通过lb再转发到对应的服务当中;而对应的签名鉴权,防刷限流机制,我们都需要在每个服务里各自都实行一遍,若通用的签名鉴权方式发生了变化,我们都需要在每个服务里都做相应的改动,总体上来说,将会导致37手游内各个服务之间的鉴权,限流防刷等机制参差不齐

(2)对于http,rpc不同协议的转换,也是由服务本身去承载,往往出现不同服务之间的调用,因为协议不互通而造成了不必要的代码调用损耗,如客户端A需要调用B服务的外部接口,而B服务恰好是RPC服务,此时若想直接调用,则必须将客户端A的调用方式改为RPC调用,或者B服务直接提供http接口供客户端A调用,这两种方式其实都是增加了编码的成本和服务端,客户端自身协议的复杂性。目前37手游为了协议的统一,直接采取的是废弃掉了RPC协议的方案

(3)域名解析管理过于混乱,之前37手游内的都是每当有一个新服务,就需要去配置一个全新的子域名,若出现客户端和服务端操作不规范(客户端直接在代码中写死域名,而不是通过读取服务端的接口动态获取时),将会导致若域名被封禁或劫持,客户端就需要去手动将代码中的域名替换并热更(实际也遇到过这样的事情)。

对于解决以上问题,有一种解决思路就是采用BFF(backend for frontend)的概念去执行。BFF 可以认为是一种适配服务,将后端的微服务进行适配(主要包括聚合裁剪和格式适配等逻辑),向无线端设备暴露友好和统一的 API,方便无线设备接入访问后端服务。

image.png 如果是以中台概念的为设计的话,BFF更特向于一个逻辑服务,负责将与业务相关的逻辑整合并返回给端上

如用户登录服务,用户登录/注册可以放到底层来实现,而对外层端上的鉴权,用户映射,风控等判断,则交由BFF去调用底层服务来实现

作为服务设计来说,这样设计能尽可能保证业务的纯粹性,而不是一坨一坨的,我们设计服务时也应该尽量保持业务的单纯性)

使用BFF虽然在业务短期内能实现“整合”和“统一”,但随着时间的推移,很多逻辑,比如安全认证,日志监控,限流熔断等,在不同的BFF下还是会出现五花八样的情况;这时候,agw就出现了,通过agw,我们便可以将一些路由,安全,认证等统一上浮。

image.png

从根本上来说,搭建api-gateway的意义可归为“统一”二字:

(1)统一流量入口:所有外部的流量均通过agw,端上只需调用agw,便可进行与内部服务的通信

(2)统一鉴权方式:内部服务可专心于业务逻辑,鉴权方式改变时,只需要改动agw即可;同时像一些私密用户态的信息,可由agw进行封装到header里,从而不用增加数据传输的风险。

(3)统一限流策略:agw可根据不同接口配置不同的限流策略,从而保证内部服务的安全性

(4)统一适配协议:无论是rpc,http或其他协议,均可通过agw调用后,进行转换。

若有一个统一的agw,域名可统一为agw的域名,指向agw后,由agw转发到各个服务上。agw自身的域名可备用多个提供给端上进行兜底措施,也可以直接采用httpdns的方式,配置agw对应的域名即可。

三,API统一网关业界内的方案对比

业界比较有名的agw的有Kong,Orange,APISX,Sheperd等等,这里先介绍最著名的Kong,和美团基于kong的设计理念实现的sheperd,eolinker基于Kong的设计理念实现的Apiinfo。

1,Kong:

(1)简介:

Kong是一款基于lua语言、nginx以及openResty开发的云原生的、平台无关、可扩展的 API 网关,以通过插件实现的高性能和可扩展性而著称。通过提供代理、路由、负载平衡、健康检查、身份验证等功能 Kong 充当了轻松编排微服务或传统 API 流量的中心层。

在github上已经有13.4K的star

(2)更确切地说,Kong 是一个在 Nginx 中运行的 Lua 应用程序,Kong 不是用这个模块编译 Nginx,而是与OpenResty一起发布,OpenResty已经包含了 lua-nginx-module

其实大白话就是——kong是一个openResty 应用程序,而OpenResty 运行在 Nginx 之上,使用 Lua 扩展了 Nginx。Kong = OpenResty + Nginx + Lua

对于kong的设计理念来说,可归结为一句话:即万物皆可插件

(3)架构图

image.png

(4)模块:

  • Kong Server :基于 nginx 的服务器,用来接收 API 请求

  • Apache Cassandra:用来存储操作数据

(5)功能:HTTP 基本认证、密钥认证、CORS,响应重写,所有你一切想到的功能都能在kong上进行实现

(6)由于篇幅原因,这里只介绍kong如何实现路由的转发,并使插件能在路由转发的过程的任意时间中执行

  • 首先,kong的内部抽象对象定义的有如下,service,route,upstream,target

  • service :是抽象层面的服务,他可以直接映射到一个物理服务(host 指向 ip + port),也可以指向一个upstream来做到负载均衡;

  • routes :是路由的抽象,他负责将实际的 request 映射到 service,Route 路由相当于nginx 配置中的location

  • upstream:相当于Kong提供了一个负载的功能,基于Nginx的虚拟主机的方式做的负载功能

    当我们部署集群时,一个单独的地址不足以满足我们的时候,我们可以使用Kong的upstream来进行设置

  • target:代表了一个物理服务,是 ip + port 的抽象,在upstream进行负载均衡的终端,当我们部署集群时,需要将每个节点作为一个target,并设置负载的权重,当然也可以通过upstream的设置对target进行健康检查。

  • 总体关系图:

image.png

  • 一次完整的转发,实际上kong会根据route找到对应的service,并判断负载均衡的节点,若有,则派发给他让他执行负载均衡(这里也只是单纯的逻辑而已,实际上lb已经做在kong里了);没有,则转发到target上

  • 由于kong本身的插件的执行判断较为复杂,笔者还未完全掌握到它的原理,因此这里先不做强行分享

2,Sherdperd:

(1)sherdperd是美团研发的一个api-gateway,虽然没有开源,但是服务治理对应是可以参考的,其设计理念跟kong应该是基本一致的

(2)架构图:

image.png

(3)Shepherd API网关的控制面由Shepherd管理平台和Shepherd监控中心组成。管理平台主要完成API的全生命周期管理以及配置下发的工作,监控中心完成API请求监控数据的收集和业务告警功能。

Shepherd API网关的配置中心主要完成控制面与数据面的信息交互,通过美团统一配置服务Lion来实现。

Shepherd API网关的数据面也就是Shepherd 服务端。一次完整的API请求,可能是从移动应用、Web应用,合作伙伴或内部系统发起,经过Nginx负载均衡系统后,到达服务端。服务端集成了一系列的基础功能组件和业务自定义组件,通过泛化调用请求后端RPC服务、HTTP服务、函数服务或服务编排服务,最后返回响应结果。

(4)对于sherdperd的具体实现文章并没有透露多少细节,但对应设计的思想还是值得看的,感兴趣的人可以去看下。

3,Apinto:

(1)简介:eolinker用golang语言写的一款统一API管理平台和网关(从本质上,本人觉得这是go版的kong),既有企业版,也有开源版,它的前身是goku(中文名:悟空 API 网关)是一个基于 Golang 开发的微服务网关,能够实现高性能 HTTP API 转发、多租户管理、API 访问权限控制等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行 API 服务治理、提高 API 服务的稳定性和安全性。),在github上有2.8k的star

(2)功能(其实仔细看就会觉得和kong的功能是很重叠的):

  • 动态路由 可通过设置locationqueryheaderhostmethod等参数匹配对应的服务
    服务发现 支持对接Eureka、Nacos、Consul
    负载均衡 支持轮询权重算法
    用户鉴权 匿名、Basic、Apikey、JWT、AK/SK认证
    SSL证书 管理多个证书
    访问域名 可为网关设置访问域名
    健康检查 支持对负载的节点进行健康检查,确保服务健壮性
    协议 HTTP/HTTPS、Webservice、Restful
    插件化 流程插件化,按需加载所需模块
    OPEN API 支持使用open api配置网关
    日志 提供节点的运行日志,可设置日志的等级输出
    请求日志 节点的请求日志输出到不同的日志接收器,现只支持file
    平滑重启 在不影响用户的情况下重启服务,完成业务代码更新
    多节点集群 使用raft算法同步集群节点配置
    Cli命令支持 通过Cli命令操控网关,插件安装、下载和网关的开启、关闭等操作均可使用一键命令操控
    支持多种插件 额外参数、参数映射、转发重写、IP黑白名单、流量控制、鉴权、响应重写、API熔断、跨域CORS,gzip压缩、access_log

(3)设计理念:其实也是万物皆插件,但他做的更绝,万物皆驱动,像一些对应的服务组件的相关的,都是以驱动的方式在服务启动的时候进行注册,当需要的时候就引用对应的方法或函数(其实就是工厂模式),对于新增的插件,则是

其本质是利用golang的piugin,在外面包了一层数据结构,当我要自定义驱动或插件时,实际上是实现了plugin,通过服务内定时去open,便可实现驱动的动态加载)

image.png

image.png

(4)插件系统图:

插件系统的插件能够在路由服务负载全局中配置。

若同一个插件在多个模块进行了配置,则配置优先级为:路由>服务>负载>全局,且插件只生效一次。
即路由和服务均配置同一个插件,最终会以路由的配置为准

image.png

(5)地址:

四,API统一网关具体在37手游该如何设计与实现?

agw的一般功能模块可归为这几项:

  • 鉴权

  • 限流

  • 路由动态配置

  • 防刷机制

  • API调用信息监控 **

  • 请求数据的重写修改(响应重写)(事前过滤,事中修改,事后过滤)

  • 自定义插件

  • 灰度流量控制

  • 协议转换

  • .....

对于37手游来说, 因为协议已经逐渐统一为http协议,因此协议转换可先不做;

对于数据的一些重写修改或一些自定义插件,可采用kong的设计理念,“万物皆插件”,通过热加载的方式来实现,但因为golang本身对插件的支持不是很好,所以可先留个口子进行搁置;

综上,我们根据我们的业务场景,构建出了我们自身的API网关,我们内部叫业务网关 具体实现可以看我们前面这篇文章

总结

总而言之,API网关对于中大型互联网公司来说,是不可或缺的,如何通过这个网关给我们带来通用的便捷性,才是实际我们要考好的事情!