《架构师》2017年4月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1 高性能API网关

阿里巴巴内部的数据分布在各个独立的业务系统中,如:商品中心、交易平台、用户中心,各个独立系统间通过HSF(High-speed Service Framework)进行数据交换。如何将这些数据安全可控的开放给外部商家和ISV,共建繁荣电商数据生态,在这个背景下API网关诞生。

1.1 总体架构

API网关采用管道设计模式,处理业务、安全、服务路由和调用等逻辑。为了满足双11高并发请求(近百万的峰值QPS)下的应用场景,网关在架构上做了一些针对性的优化:

元数据读取采用富客户端多级缓存架构,并异步刷新缓存过期数据,该架构能支持千万级QPS请求,并能良好的控制机房网络拥塞。

同步调用受限于线程数量,而线程资源宝贵,在API网关这类高并发应用场景下,一定比例的API超时就会让所有调用的RT升高,异步化的引入彻底的隔离API之间的影响。网关在Servlet线程在进行完API调用前置校验后,使用HSF或HTTP NIO client发起远程服务调用,并结束和回收到该线程。待HSF或者HTTP请求得到响应后,以事件驱动的方式将远程调用响应结果和API请求上下文信息,提交到TOP工作线程池,由TOP工作线程完成后续的数据处理。最后使用Jetty Continuation特性唤起请求将响应结果输出给ISV,实现请求的全异步化处理。线程模型如图1所示。

图1 API网关全异步化调用模型

1.2 多级缓存富客户端

在API调用链路中会依赖对元数据的获取,比如需要获取API的流控信息、字段等级、类目信息、APP的密钥、IP白名单、权限包信息,用户授权信息等等。在双11场景下,元数据获取QPS高达上千万,如何优化元数据获取的性能是API网关的关键点。

千万级QPS全部打到DB是不可取的,尽管DB有做分库分表处理,所以我们在DB前面加了一层分布式缓存;然而千万级QPS需要近百台缓存服务器,为了节约缓存服务器开销以及减少过多的网络请求,我们在分布式缓存前面加了一层LRU规则的本地缓存;为了防止缓存被击穿,我们在本地缓存前面加了一层BloomFilter。一套基于漏斗模型的元数据读取架构产生(如图2所示)。缓存控制中心可以动态推送缓存规则,如数据是否进行缓存、缓存时长、本地缓存大小。为了解决缓存数据过期时在极端情况下可能出现的并发请求问题,网关会容忍拿到过期的元数据(多数情况对数据时效性要求不高),并提交异步任务更新数据信息。

图2 基于漏斗模型的元数据读取

1.3 高性能批量API调用

在双11高并发的场景下,对商家和ISV的系统同样是一个考验,如何提高ISV请求API的性能,降低请求RT和网络消耗同样是一个重要的事情。在ISV开发的系统中通常存在这样的逻辑单元,需要调用多个API才能完成某项业务(如图3),在这种串行调用模式下RT较长同时多次调用发送较多重复的报文导致网络消耗过多,在弱网环境下表现更加明显。

图3 串行API调用处理流程

API网关提供批量API调用模式(如图4所示)缓解ISV在调用RT过高和网络消耗上的痛点。ISV发起的批量请求会在TOP SDK进行合并,并发送到指定的网关;网关接收到请求后在单线程模式下进行公共逻辑计算,计算通过后将调用安装API维度拆分,并分别发起异步化远程调用,至此该线程结束并被回收;每个子API的远程请求结果返回时会拿到一个线程进行私有逻辑处理,处理结束时会将处理结果缓存并将完成计数器加一;最后完成处理的线程,会将结果进行排序合并和输出。

图4 批量API调用处理流程

1.4 多维度流量控制

TOP API网关暴露在互联网环境,日调用量达几百亿。特别是在双11场景中,API调用基数大、调用者众多以及各个API的服务能力不一致,为了保证各个API能够稳定提供服务,不会被暴涨的请求流量击垮,那么多维度流量控制是API网关的一个重要环节。API网关提供一系列通用的流量控制规则,如API每秒流控、API单日调用量控制、APPKEY单日调用量控制等。

在双11场景中,也会有一些特殊的流量控制场景,比如单个API提供的能力有限,例如只能提供20万QPS的能力而实际的调用需求可能会有40万QPS。在这种场景下怎么去做好流量分配,保证核心业务调用不被限流。TOP API网关提供了流量分组的策略,比如我们可以把20万QPS的能力分为3个组别,并可以动态去配置和调整每个组别的比例,如:分组1占比50%、如分组2占比40%、分组3占比10%。我们将核心重要的调用放到分组1,将实时性要求高的调用放到分组2,将一些实时性要求不高的调用放到分组3。通过该模式我们能够让一些核心或者实时性要求高的调用能够较高概率通过流量限制获取到相应的数据。同时TOP API网关是一个插件化的网关,我们可以编写流控插件并动态部署到网关,在流控插件中我们可以获取到调用上下文信息,通过Groovy脚本或简单表达式编写自定义流控规则,以满足双11场景中丰富的流控场景。

使用集群流控还是单机流控?单机流控的优势是系统开销较小,但是存在如下短板:

1. 集群单机流量分配不均。

2. 单日流控计数器在某台服务器挂掉或者重启时比较难处理。

3. API QPS限制小于网关集群机器数量时,单机流控无法配置。基于这些问题,API网关最开始统一使用集群流控方案,但在双11前压测中发现如下一些问题:

4. 单KEY热点问题,当单KEY QPS超过几十万时,单台缓存服务器RT明显增加。

5. 缓存集群QPS达到数百万时,服务器投入较高。

针对第一个问题的解法是,将缓存KEY进行分片可将请求离散多台缓存服务器。针对第二个问题,API网关采取了单机+集群流控相结合的解决方案,对于高QPS API流控采取单机流控方案,服务端使用Google ConcurrentLinkedHashMap缓存计数器,在并发安全的前提下保持了较高的性能,同时能做到LRU策略淘汰过期数据。