【1】高性能网关--api限额设计

270 阅读4分钟

一、背景

    网关的出现用来帮助后端开发者减少通用工作量的开发,如rpc接口的限流,限额,鉴权等。本文提供一种利用spring cloud gateway来实现的限额实现方案。

二、概念

    通常网关都要对接第三方调用者。由于三方开发者的不确定性,为了避免对网关整体性能的影响,需要对三方调用者进行限额处理。限额的概念可以理解为,对于网关提供的某个api在一定时间段内只能调用多少次。比如:一天内限制调用500万次等。超过这个次数后再次请求api就会报错。

三、整体架构

网关限额图.png

    网关运行过程中,记录每次调用,所有的计数流程都是在本地进行,通过异步的方式将所有网关的限额情况上传到redis中。同时通过redis同步某个api中的调用请求,如果发现额度超标,则在网关侧直接拒接请求。
    启动过程中,从数据库中加载不同维度的限额次数。

四、实现流程

    在spring cloud gateway中新增一个QuotaLimitGatewayFilterFactory的过滤器。对于配置了限流策略的api调用该过滤器完成额度的判断。整体流程如下所示。

限额流程图.png

    请求打到网关后,先根据不同维度将本地api请求次数加1。然后本地判断当前调用次数是否查过额度限制,如果超过,则直接返回报错信息。反之则放行,然后通过异步的方式同步本地跟远程redis的打到其他网关上的相同api的限额次数。     以api为维度进行设置,一个api可能对应多个quota_key,根据传入请求中包含的quota_key来进行额度判断。同一个api不同quota_key之间是与关系,只有全部通过,校验才算通过。
    redis中存储的数据结构如下所示。

类型:hash
key: apiid_key1:value1;key2:value2_计算出来的数字
key1-value1: usedQuota-123 // 已经请求的额度
key2-value2: expireTime-1638753782000 // 时间戳:将要过期的时间
key3-value3: totalQuota-1000 // 总的额度
key4-value4: startTime-1638753782000 // 开始的时间戳

    redis中key的构成为apiid,一般为网关提供的api路径。key1:value1表示的是对该api的不同请求维度。比如一般都会先给第三方的调用者颁发一个appkey用来标识不同调用者的身份。如果想对某个第三方进行限额key1:value1可以设置为appkey:ak334436876。表示对appkey为ak334436876的进行限额。
    key中“计算出来的数字”规则如下:当前时间(秒)/ 时间跨度(秒)。增加此字段是为了解决本地跟远程同步过程存在的不一致性问题。

数据库结构如下所示。

CREATE TABLE `api_quota_limit_config` (
  `id` bigint(20) NOT NULL,
  `api` varchar(255) NOT NULL COMMENT 'api名称',
  `quota_key` varchar(255) NOT NULL COMMENT '限额维度,针对api,api有多个维度',
  `quota` int(11) NOT NULL COMMENT '限额大小',
  `time_span` int(11) NOT NULL COMMENT '时间跨度,例如一小时的限额为5000,则填写3600,单位s',
  `create_time` bigint(20) DEFAULT NULL COMMENT '创建时间-时间戳',
  `update_time` bigint(20) DEFAULT NULL COMMENT '更新时间-时间戳',
  PRIMARY KEY (`id`),
  KEY `api_quota_key` (`api`,`quota_key`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8

通过异步线程的方式定时更新数据库中的配额信息。

4.1 计数流程

    流量到来时,先在本地计数,定时同步和拉取redis中的最新值。具体同步流程如下。
1、初始化时本地local计数为0,远程remote计数为0。
2、请求到来时,本地local变量计数增加,远程计数不变。
3、异步同步远程变量时,先计算出本机的增加值localIncr = local - remote。
4、将localIncr同步增加到远程,使用redis的原子incr来增加。
5、将最新的远程计数latestRemote同步到本地。计算出相同请求打到其他机器上的次数。otherIncr = latestRemote - local。
6、更新本地最新远程值为latestRemote。 localLatestRemote = latestRemote。
7、更新本地local,将otherIncr增加到local。 local = local + otherIncr。

4.2 gateway中配置格式

gateway中可以配置多种维度的限额策略,其中每个维度为一个key,不同key之间以英文;分割。举例假设要限制appkey为ak334436876且用户id为123的限额是10000次每小时,api整体维度的限额是20000次每小时。则在网关中的配置如下所示。

keys: appkey,userId;apiId

数据库中有如下记录

idapiquota_keyquotatime_span
1qury.user.infoappkey:ak334436876,userId:123100003600
2qury.user.infoapiId: qury.user.info200003600

五、代码地址

github.com/Tyoukai/fla…
测试的curl:

curl --location --request GET 'http://localhost:8100/gateway/quotaTest?userId=6666'