浅谈API网关

 前言  

        随着微服务架构的流行,API网关逐渐进入人们的视野,并且越来越受到欢迎。在微服务体系架构中,我们将应用程序划分为多个低耦合的服务。每个服务都具有特定的功能,并交给不同的团队维护。尽管微服务具有许多优势,比如程序易于开发、维护和部署,将大团队拆分成小团队利于敏捷实践落地等,但是也带来一些问题,最为直观的就是由于接口过于繁杂,客户端难以快速、安全地访问到所需的信息。那么如何解决呢?这种情况下,我们就需要本文所分享的API 网关(API Gataway)。

1.1 接入层的演进过程

        在正式介绍API网关之前,简单梳理里一下接入层的技术演进过程。

图片

(1)单机架构:通过DNS-server,域名解析到ip,通过ip直接访问

图片

(2) DNS轮询:暴露了太多ip

图片

(3)Ngnix反向代理:ngnix为单点

图片

(4) keepalived:解决高可用的问题

图片

(5) LVS:通过keepalived+VIP的方案可以保证可用性,水平扩展问题

图片

(6) DNS轮询:水平扩展,才是解决性能问题的根本方案

        上图方案6为我们提供了一种高可用可扩展的架构,但是我们发现似乎还是缺少了“一层”:

  1. 实际业务中,每个业务都会需要鉴权、限流、权限校验等逻辑,这里提到的接入流程没法解决,服务端自行实现在质量上难以保证。

  2. 随着业务越来越复杂,打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,另一方面是连接数的瓶颈。

  3. 每上线一个新的服务,都需要运维参与,申请域名、配置Nginx等,当上线、下线服务器时,同样也需要运维参与,另外采用域名这种方式,对于环境的隔离也不太友好,调用者需要自己根据域名自己进行判断。

  4. 后端每个微服务可能是由不同语言编写的、采用了不同的协议,比如HTTP、Dubbo、GRPC等,但是你不可能要求客户端去适配这么多种协议,这是一项非常有挑战的工作,项目会变的非常复杂且很难维护。

    以上,这缺失的这一层正是接下来要介绍的API网关。

1.2  API网关相关技术

1.2.1 路由匹配

图片

        路由是 API 网关的生命,没有高性能的路由,就没有快速的匹配过程,API 网关的性能无法提升。只有路由是 100% 参与请求的,因此路由必须要高性能。同时路由匹配条件也要足够灵活和强大,除了要支持最基本的 uri、host,其他可选的如 IP 地址、请求参数、请求头、Cookie 等也需要支持。

1.2.2 反向代理

图片

        什么是代理?代理其实就相当于交易双方的中间商,当客户端想要与服务端进行“交易”时,代理就需要充当中间商的身份来完成这一次交易。因为交易双方是两个主体,当中间商为客户端服务时,该中间商就是正向代理,中间商为服务端服务时,该中间商就是反向向代理。

图片

(1)正向代理

图片

(2)反向代理

        在API网关中我们所需要实现的主要是反向代理,实现反向代理的方式有多种,这些实现方式的差异也会体现在具体的API网关实现差异上。常见的反向代理的几种实现方式如下:

图片

        

1.2.3 负载均衡

图片

        负载均衡是实现高可用的一项重要技术,可理解是一种将请求“均衡”分配到不同的机器上的技术,目的是提升系统整体的负载能力。从单机网站到分布式网站,解决了大型网站访问量大,并发量高,海量数据的问题。

  • 4层负载均衡

        常见的4层协议:TCP/UDP。L4 四层负载均衡工作于处于 OSI 模型的传输层,主要工作是转发。它在接收到客户端报文后,需要了解传输层的协议内容,根据预设的转发模式和调度算法将报文转发到应用服务器。以 TCP 为例,当一个 TCP 连接的初始 SYN 报文到达时,调度器就选择一台服务器,将报文转发给它。此后通过查发报文的 IP 和 TCP 报文头地址,保证此连接的后继报文被转发到该服务器。

  • 7层负载均衡

        常见的 7 层协议有 HTTP(S)、HTTP2、Dubbo、QUIC、MQTT 等。L7 七层负载均衡工作在 OSI 模型的应用层,主要工作就是代理。七层负载均衡会与客户端建立一条完整的连接并将应用层的请求解析出来,再按照调度算法选择一个应用服务器,并与应用服务器建立另外一条连接将请求发送过去。

  • 负载均衡技术

图片

  • DNS

图片

1.2.4 泛化调用

图片

  • 什么是泛化调用?
             泛化调用是指在调用方没有服务方提供的API(SDK)的情况下,对服务方进行调用,并且可以拿到调用结果。

  • 什么时候会用到泛化调用?

    • 测试集成平台
                 我们要搭建一个统一的测试平台,可以让各个业务方在测试平台中通过输入接口、分组名、方法名以及参数值,在线测试自己发布的RPC服务。这时我们就有一个问题要解决,我们搭建统一的测试平台实际上是作为各个RPC服务的调用端,而在RPC框架的使用中,调用端是需要依赖服务提供方提供的接口API的,而统一测试平台不可能依赖所有服务提供方的接口API。我们不能因为每有一个新的服务发布,就去修改平台的代码以及重新上线。这时我们就需要让调用端在没有服务提供方提供接口的情况下,仍然可以正常地发起RPC调用。

    • 网关服务
                 我们要搭建一个轻量级的服务网关,可以让各个业务方用HTTP的方式,通过服务网关调用其它服务。API网关在进行协议转换的时候有两步,第一步是把客户端的请求协议转为相应微服务的接口协议,然后再调用相应的微服务;第二步是把微服务返回的结果转换为客户端的协议,然后返回给客户端。客户端的请求协议可能有多种,而微服务的接口协议也可能有多种,不同的协议之间是无法直接通讯的,那么就需要一个模块来负责把不同的协议转换为用某种语言描述的对象,然后把这个对象再转化为相应的协议代码块。如下图所示:

图片

02

 什么是API网关?  

        API网关是随着微服务(Microservice)概念兴起的一种架构模式,是运行于外部请求与内部服务之间的一个流量入口,实现对外部请求的协议转换、鉴权、流控、参数校验、监控等通用功能。客户端统一发送请求到网关层,再由网关层进行路由转发,使客户端访问接口的复杂度大大降低,主要的功能如下图所示。

图片

03

 API网关实战  

        以下介绍两个API网关,Kong,Zuul,两者在底层实现上存在较大的差异。本文重点从路由(router),负载均衡(balancer),泛化调用, 插件(plugin)等方面加以简单介绍。

3.1 Kong

        Kong 是 Mashape 开源的基于 Nginx_Lua 模块写的高可用服务网关,由于 Kong 是基于 Nginx 的,所以可以水平扩展多个 Kong 服务器。通过前置的负载均衡配置把请求均匀地分发到各个 Server,来应对大批量的网络请求。它提供了http请求路由,后端服务负载均衡以及通过丰富的插件提供认证鉴权、流量调控、日志监控等功能。

3.1.1 Kong架构

图片

  • Kong的核心架构

图片

  • Kong 主要有三个组件

图片

3.1.2 Kong核心模型

图片

  • 核心模型说明

图片

  • 核心模型的关系

图片

3.1.3 Kong核心流程

图片

        当Kong运行时,每个对API的请求将先被Kong命中,然后这个请求将会被代理转发到最终的API接口。在请求(Requests)和响应(Responses)之间,Kong将会执行已经事先安装和配置好的任何插件,对请求进行处理。Kong请求的生命周期和OpenRestry的生命周期息息相关,下表对二者之间的关系进行简单的介绍。

图片

3.1.4 Kong路由匹配

图片

        Kong的整个路由匹配流程分为两部分:

  • 第一部分是系统启动初始化时对路由分类,建立索引。

图片

  • 第二部分时请求到来时,根据请求的特征去查找相关路由:Kong的路由匹配主要采用请求的host、url和method三元组来对请求进行分组,从而找到应该匹配的API。第一次匹配可能比较慢,后续存入缓存后,匹配速度就很快。Kong是在access的时候进行匹配的。

    图片

    在某些特殊情况下,多个路由符合匹配条件的数目也会完全一致。此时应该按照如下优先级选择路由。

    (1)hosts属性比通配符优先级高。

    (2)headers属性中有更多匹配值的路由优先级高。

    (3)paths属性中包含正则表达式的路由比包含普通前缀路径表达式的路由优先级高。

    (4)匹配路径较长的路由优先级高。

    (5)创建时间更早的路由优先级高。

3.1.5 Kong泛化调用

图片

  • kong-plugin-grpc-web:在代理grpc服务的时候,我们需要将此grpc服务以http/https协议提供给前端访问,因此需要用到Kong提供的kong-plugin-grpc-web插件来帮忙将http/https的请求代理到后端的grpc服务上。kong-plugin-grpc-web

    图片

  • Dubbo Proxy:HTTP服务请求经过Kong网关,配置多个Target Dubbo Proxy服务器,服务事先可以定义在Kong网关中。Dubbo Proxy监听Dubbo服务注册中心,DUBBO服务发生相应变化时,Dubbo Proxy可以随时将不工作的服务剔除掉。当Dubbo Proxy发生故障不工作时,通过Kong Health检查功能,将不正常工作的或无法工作的Dubbo Proxy剔除掉。

图片

3.1.6 Kong负载均衡

图片

        Kong使用了DNS-Balancer和Ring-Balancer两种均衡模式。DNS-Balancer适合对外部的第三方服务进行负载均衡。候选服务器列表添加在A记录或SRV记录中。A记录中只包含IP地址,而且没有权重信息,适合简单的round-robin。SRV记录中包含IP地址和端口,并含有权重信息,可以按权重调拨流量以及IP地址复用。Ring-Balancer适合对内部服务进行负载运行。因为这种模式的控制性强,可以灵活地支持机器上下线操作、带权重的负载均衡以及可以进行蓝绿部署和金丝雀发布。

  • DNS-Balancer

            当使用基于DNS的负载均衡策略时,所有后端服务的注册操作都是在Kong网关之外完成的。Kong服务器仅接收来自DNS服务器的更新。用户除配置Kong网关外,还需自行配置DNS服务器。在Kong网关中,所有服务的host属性配置为主机名而非IP地址的服务。所有服务都会自动使用基于DNS的负载均衡策略。基于DNS的负载均衡策略生效的前提是:主机名不能解析为上游服务的名称,即不能与上游服务中的name属性名称一致,或者不能出现在DNS服务器的hosts文件中。DNS解析器将按顺序解析以下记录类型:

    (1)上次解析的最后一次成功类型(LAST)

    (2)SRV记录

    (3)A记录

    (4)CNAME记录

    该顺序可通过dns_order配置属性进行配置。解析不同记录类型的顺序。 LAST类型表示上次成功查找的类型(用于指定的名称)。格式是一个(不区分大小写)逗号分隔的列表。默认值:LAST,SRV,A,CNAME

  • Ring-Balancer(环状负载均衡)
             在Kong里面,Ring-Balancer通过upstream和target对象描述。upstream对象用于调节负载均衡的行为,如权重分配、健康检查以及是否通过一致性hash绑定请求到对应的机器上。target存储进行负载均衡的机器列表。

3.1.7 Kong插件

图片

        网关对api的处理是通过一组插件完成的,插件是网关的核心,大部分功能特性都可以基于插件的形式提供,组件化可以有效提高网关的扩展性。当前网关默认提供了一些基本插件,如鉴权、限流、监控,完成一些非业务功能,减少业务侧开发成本,方便使用。  

        Kong每个插件都需要放置在plugins目录下。这里有个base_plugin.lua文件,定义了一个每个插件都需要去继承的基类,该基类定义了一些每个子类都需要去实现的方法。插件需要定义一个handler.lua文件,在这个文件里面去实现子类。此外还需要定义一个schema.lua文件,在这里定义每个插件的配置数据。在nginx的master启动阶段,会扫描plugins目录下面的所有插件,加载handler.lua和schema.lua中定义的模块,见kong/init.lua。

        数据库中的plugins表定义了需要启用的插件。只有在这张表里面存在的插件,并且是开启状态的才会被使用。

        在每个请求的执行阶段,都会先执行kong的前置处理方法,然后遍历所有启用的插件执行每个阶段对应的方法。遍历插件的时候会从缓存里面加载插件配置,如果缓存失效就会从数据里面重新加载一遍。见core/plugins_iterator.lua。

图片

整个插件的加载执行流程主要集中在kong/init.lua和kong/core/plugins_iterator这两个文件中。

图片

  • 插件生命周期管理

图片

  • 以下为一些官方或开源的插件

图片

3.2 Zuul

Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。

3.2.1 Zuul架构

图片

  • Zuul1 是基于 Servlet 框架构建,如图所示,采用的是阻塞和多线程方式,即一个线程处理一次连接请求,这种方式在内部延迟严重、设备故障较多情况下会引起存活的连接增多和线程增加的情况发生。

  • Zuul2 的巨大区别是它运行在异步和无阻塞框架上,每个 CPU 核一个线程,处理所有的请求和响应,请求和响应的生命周期是通过事件和回调来处理的,这种方式减少了线程数量,因此开销较小。

图片

3.2.2 Zuul核心流程

图片

  • 前置过滤器(Pre Filters)主要处理请求还未到达后端服务之前的工作,集成鉴权、限流、参数校验、反爬、日志、统计、监控、协议转换等功能,支持业务方自定义Filter。

  • 路由过滤器(Routing Filters)将请求通过泛化调用方式发往内部服务。获得RPC框架所支持的能力。

  • 后置过滤器(Post Filters)处理跨域、日志、统计、监控、协议转换等功能。

  • 错误过滤器(Error Filters)处理调用失败的返回结果。

3.2.3 Zuul路由匹配

图片

Route过滤器:

  • RibbonRoutingFilter:使用Ribbon、Hystrix、以及可插拔的HTTP客户端发送请求。通过RequestContext的FilterConstants.SERVICE_ID_KEY属性来获取ServiceId。该过滤器可使用不同的HTTP客户端:

    • Apache HttpClient:默认客户端。

    • Squareup OkHttpClient v3:通过添加com.squareup.okhttp3:okhttp包,并设置ribbon.okhttp.enabled=true来启用。

    • Netflix Ribbon HTTP client: 通过设置ribbon.restclient.enabled=true来启用。该客户端有一些限制,其中包括不支持PATCH方法,但它内置了重试功能。

  • SimpleHostRoutingFilter:通过Apache HttpClient向预先确定的URLs发送请求。这里的URLs通过RequestContext.getRouteHost()来获取。

3.2.4 Zuul泛化调用

图片

图片

组件功能
Eureka各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还3可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里
Ribbon服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台
Feign基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
Hystrix发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
Zuul如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务

3.2.5 Zuul负载均衡

图片

        当 Zuul 收到请求时,它会选择一个可用的物理位置并将请求转发到实际的服务实例。缓存服务实例位置并将请求转发到实际位置的整个过程是开箱即用的,无需额外配置。

图片

        Zuul 使用 Netflix Ribbon 从Eureka Server中查找服务的所有实例。

3.2.6 Zuul插件

图片

        Zuul的一个重要机制就是Filter动态加载机制。Zuul支持的动态Filter由Groovy代码编写,动态管理Groovy的File目录变更并动态编译和加载。

图片

  • Filter类文件动态管理

        Zuul通过FilterFileManager组件监控存放Filter文件的目录,定期扫描这些目录,如果发现有新Filter源码文件或者Filter源码文件有改动,则对文件进行编译和加载。FilterFileManager管理目录轮询的变化和新的Groovy过滤器。轮询间隔和目录在类的初始化中指定,并且轮询器将进行检查、更改和添加操作。

  • Filter类文件动态编译

        Zuul动态加载Filter文件,并通过编译器将文件编译成Class,目前Zuul通过定义DynamicCodeCompiler接口及Groovy编译的实现类GroovyCompiler 来 完 成 Groovy 编 写 的 Filter 的 动 态 编 译 。

  • Java类型动态Zuul Filter加载

        基于Zuul的字节码加载机制和File文件扫描与类文件动态加载机制,也支持基于Java语言的动态Zuul Filter加载。

04

 总结  

  • API网关都包含了一些共同的核心功能,比如路由匹配、负载均衡、泛化调用、扩展插件等。在实现的时候侧重点各功能均有不同。

  • 基于servlet或者Ngnix是API网关实现的两种主要技术。

  • Nginx功能强大,在API网关领域被广泛运用。除Kong外,APISIX等开源网关也是基于Nginx(OpenResty),如下图可见不同API网关实现思想大同小异。

图片

05

 参考资料 

[1] https://github.com/qianyugang/kong-docs-cn

[2] Router and Filter: Zuul https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html

[3] Zuul开发者指南https://jbone.cn/translate/spring-cloud-netflix-zuul/zuul-developer-guide.html

[4] 什么是DNS SRV记录 https://www.cloudflare.com/zh-cn/learning/dns/dns-records/dns-srv-record/

[5] Apache中文文档:https://www.docs4dev.com/docs/zh/apache/2.4/reference/howto-reverse_proxy.html

[6]Kong网关:入门、实战与进阶. 孔庆雍 著

[7] https://apisix.apache.org

[8] https://www.gravitee.io/

[9] https://docs.gravitee.io/apim/3.x/apim_overview_architecture.html

[10] http://openresty.org/cn/

[11] https://github.com/openresty/lua-nginx-module

[12] https://cloud.tencent.com/developer/article/1760949

[13] Router and Filter: Zuul: https://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html

[14] https://www.baeldung.com/zuul-load-balancing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值