作者 | *飞 阿里云开放平台技术专家,负责阿里云OpenAPI网关建设,致力于为用户提供高可用的OpenAPI服务。
引言
随着互联网技术以及微服务、Service Mesh的发展,API在近几年变得越来越重要,大部分的企业都开放了API,绝大多数的技术能力也可以通过API快速获取,比如图像识别、语音识别、资源操作等。
要开放API,就会有一个API网关,互联网上每天数以亿次的API调用背后,除了有具体的API Service提供服务外,还有API 网关作为统一出入口来处理这些流量。
网关形态
API 网关一般分为:
- 中心化
- 去中心化
两类,主要区别如下:
得益于开发模型更简单、更易维护、易接入、且有多种手段来保障稳定性,中心化的网关服务更常见,被很多大型企业、API网关服务提供厂商采用,我们本次将分享中心化网关的建设。
网关选型
要开放API,常见的解决方案有3种:
- 云上的API Gateway类云服务,比如API Gateway,提供了开箱即用的API 网关服务;
- 基于开源API网关二次开发,如Kong;
- 从零自建。
三种解决方案的对比如下:
核心能力
要建设一个企业级的API网关绝非一件易事,需要大量的技术持续投入,由于篇幅有限,我们在这里只介绍几个最核心能力的落地思路:
- 网络层安全防护;
- 应用层流量控制;
- 稳定性。
网络层安全防护
作为流量的统一出入口,网关是很容易受到各类攻击的,有一些是恶意攻击,比如游戏等行业就更容易受到一些恶意的攻击,也有一些是无意的带有攻击行为的流量,比如突发活动导致的大流量。
常用的防护手段有:
- 四层防护;
- 七层防护;
- 流量清洗。
四层、七层防护是两块涉及到攻防的专业领域,比如常见的有DDOS、基于IP的CC攻击等,这块需要有专门的技术投入建设的,如果没有资源投入的话,建议购买云上的高防服务来防护,要避免裸奔,没有任何防护的互联网应用是非常危险的,尤其是对于网关来说。
以基于HTTP协议的API为例:
- 我们需要针对不同的出口IP配置流量阈值,避免某个客户端出口IP占用过多流量而影响整个Endpoint,如果可以识别出用户身份还可以基于用户身份配置流控阈值
- 如果为不同API服务提供了不同的Endpoint,我们还需要为每个Endpoint配置独立的流量阈值,避免单个Endpoint流量过大影响同集群的其他Endpoint
- 除此之外,网络层还需要对后端Real Server进行保护,需要为后端集群配置集群防护阈值,避免有超过集群容量的流量进入集群,这里建议配置为基于单个Server的流控阈值,这样当集群动态扩缩容的时候这个阈值会自动变更,而不是配置为绝对值。
有了四层、七层的防护,基本的防护能力就已经具备了,如果我们为每一个API服务或者每一个用户分配了一个Endpoint,通常多个Endpoint的流量会流入到同一个LVS设备,这个LVS设备受到四层攻击的时候我们就无法判断是由于哪个Endpoint导致的,这个时候就需要流量清洗能力,来分析出是攻击者是通过哪个Endpoint作为攻击点的,由于此类攻击一般都是自动跟随攻击,服务端切换一个新的LVS设备后攻击就会跟过来。应对措施主要流程如下:
- 使用二分法,将域名分成2份分别解析到2个LVS上;
- 判断攻击跟随到了哪个LVS上;
- 将跟随到的这个LVS继续使用二分法进行拆分,直到定位到攻击流量跟随的是哪个Endpoint;
- 将这个Endpoint拉到黑洞。
这个流量清洗方案通常可以自己开发完成,但是需要有一个用于清洗的LVS设备池子,至此在网络层的防护就比较全面了,包含了IP、用户、Endpoint、集群、流量清洗。
应用层流量控制
通过网络层防护对网关有了比较全面的防护,网关的处理能要比每个后端Service的处理能力要强,网关作为流量入口除了要保护好网关自身,还好保护好后端Service不受攻击,这个时候就要使用网关应用层流量控制了,在网关层需要支持为以下维度配置流控:
- 为每个Service租户配置流控阈值,确保每个后端服务收到的流量不会超过Service集群容量;
- 为每个API配置流控阈值,避免某个API占用了单个Service过多流量;
- 为每个用户配置Service、API粒度的流控阈值,避免单个用户占用过多流量;
- 为特殊用户配置特殊的流控配置,比如大用户等。
过去,被很多人问到了应用层要如何做流控,要做一套完善的流控机制,还是需要不少投入的,对技术也有比较高的要求,流控一般有两个相互牵制的因素:性能、准确性,尤其是当单次请求流控维度非常多的时候,性能问题就会凸显出来,尤其是还要保障精度的时候,常用的方案有:
- 精准流控,基于Redis等分布式缓存的计数器能力进行控制;
- 单机流控,纯单机流量控制;
- 单机+分布式两段流控;
- 集群内部流控。
基于分布式缓存的计数器模式是比较简单的流控方案,可以做到精准流控,但是当调用分布式缓存延迟比较高+流控维度过多后性能就会成为问题,这个地方有个技巧,缓存的KEY可以设置为业务Key+时间戳,比如秒级流控可以设置为:XXX_YYYYMMDDHHMMSS,根据这个Key通过调用一次远程计数器的Inc接口就可以完成一次精准流控,当业务体量不大,维度不多,但是需要支持精准流控的时候可以使用此方案。
纯单机的流控也是一个比较常见的方案,由于不涉及到远程IO,性能是最好的,通常用于非精准流控场景,因为不均衡的流量分布、扩容、缩容等场景都会对流控准确度造成较大的影响,将流控总阈值平均分布到每个Server上,可以通过后台手动设置集群数量的方式,也可以使用自动化的集群Server数量自动感知模式来感知集群总Server数。
单机+分布式缓存的两段式流控是集上面两种方案的优点的一种平衡解决方案,当流控水位比较低的时候采用单机流控,以取得更好的性能,当流控水位到达一定水位快满的时候,汇总所有本地流控计数信息后累加到分布式计数器中,并切换到分布式计数模式进行精准流控,此方案整体上在保障高精度的同时换取了更好的性能表现,通常分钟、小时、天级别的流控更适合采用这种模式。
集群内部流控方式,也是一种精准流控模式,即在当前本地Server集群中选举一个计数Server,将远程计数IO切换到集群内部通信,在做到精准流控的同时获取更好的性能,这个方式在技术上会有更大的挑战,比如当技术Server重启、故障后如何保障精度等。
流控方面在开源方面已经有了很多的解决方案,比如阿里开源的Sentinel综合实力是非常强的,支持多种计数模式,包含集群计数模式、滑动窗口等,其他开源解决方案还有Hystrix、resilience4j等。
稳定性
网关对于稳定性SLA的要求是要比其他任何单个Service都要高的,比如网关的SLA是5个9,那么其他所有通过网关开放的API的SLA都不可能高于5个9。尤其是对于中心化部署的网关,一旦网关故障,造成的影响也是非常大的。所以需要通过一系列的稳定性保障体系来保障网关的稳定性,而不是只做到某个点就可以的,其中比较核心的几个点有:
租户隔离:
网关作为一个平台,会有很多租户接入,即不同的Service提供者,网关要做到不同租户之间的隔离,不能相互影响,典型的反例是:网关是基于线程模型的同步调用,且多个Service在同一个集群提供服务,当某个Service有大量慢调用的时候就会耗尽所有网关层的线程资源,导致整个集群中的服务不可用,导致雪崩。
- 首先网关需要基于异步的模型,或者使用协程,网关做为一个网络IO密集型应用,一定要避免被某个远程调用影响。
- 在租户层面可以考虑基于容器的隔离,为所有接入租户或者重要租户进行物理隔离。
- 对链接进行隔离,比如基于HTTP的连接池,要为每个host设置默认的最大连接数量。
- 基于线程池的隔离,实际在网关中应用较少,如果有适合的场景也可以考虑使用
熔断降级:
对于依赖的服务要有熔断降级机制
- 网关的主要依赖就是后端Service,后端Service通常也是最不稳定的,比如高RT、大Request、大Response等。当某个后端服务、API指定异常数量超过一定数量后要自动降级,只放小流量到后端Service,通过小流量探测后端恢复正常后再恢复服务,避免单个Service、API的异常流量耗尽系统资源。
- 除了Service外就是数据源、分布式缓存、认证系统等,当这些依赖服务异常后网关要有逃生能力,通常可以通过多级逻辑+物理缓存解决,比如本地缓存、分布式缓存、磁盘文件兜底等方案,当依赖服务故障后对于存量的业务网关依然可以继续提供服务。
熔断也有一些开源的解决方案,比如阿里开源的Sentinel,除了支持流控,在熔断方面也是非常优秀的。
巡检、自动化回归测试:
网关作为开放API的平台,所有的能力都可以通过API调用完成自动化的测试、验证,是最适合做自动化回归测试、自动巡检的系统,一定要确保网关系统有完善的自动化测试Case,当API发布过程中进行验证,并在日常做巡检,一旦系统能力有任何问题,可以第一时间收到报警。
容灾演练:
将容灾演练日常化,在大版本发布的时候要有容灾测试,确保新代码没有破坏系统的容灾能力。
除了这些以外,还要结合前面讲到的安全防护能力一起保障网关的稳定性
总结
要建设一个企业级的网关要先根据当前的环境选择适合的方案,不是所有的企业都需要自己从零建设自己的网关,一旦决定要做,是需要持续的技术投入的,本文只是重点介绍了建设网关中最重要的稳定性部分,作为网关稳定一定是第一位的,也是最难做的部分,本文作为一个引子,后续将会针对不同的细分领域进行分享。