限流介绍

我们在API网关中已经介绍了,限流是保护网关的手段之一,和身份认证以及鉴权一起组成安全防御大闸。其目的是对并发请求进行限速或限制一个时间窗口内请求的数量, 一旦达到阈值就排队等待或降级甚至拒绝服务。

为啥要做限流

  • 在API网关接受的请求量超过了最大处理能力后,网关就会出现问题,不能在正常提供服务,所以要限流。
  • 为了防止其他人通过DDOS恶意攻击网关。
  • 某些API处理时很消耗系统资源,频繁的调用会造成系统瘫痪,所以要控制这些API接口的调用频率。
  • 在API网关是面对公司外部使用的情况下,通过限制API调用频率来减少不必要的资源浪费。
  • 根据重要性给客户端用户划分不同的级别,级别越低,流量限制越严格。

限流的几种实现方式

根据上面列出的原因,我们自然知道限流该怎么限制法,但是具体要怎么实现呢?也就是该怎么设计算法来实现呢?

计数器

网关每接受到一个请求,计数器就增加1,每处理完一个请求,计数器就减少1,计数器有最大值和最小值,最小值是0,最大值根据需要来设定,最大值相当于缓冲区,超过这个缓冲区,网关将不再接受请求。

由于请求会由不同的线程或者协程处理,所以计数器需要放在可以共享的地方,比如说内存数据库Redis里面。

进一步的,如果我们想细粒度的控制流量,就需要设置多个不同的计数器,比如,某些特定的API需要设置特定的计数器,不同的客户端要设置不同的计数器。除此以外网关还要解析请求头里面的信息,比如IP来判断属于哪个用户等等。

这种方法有个缺点,如果我们想要在一分钟内限制请求1000次的话,用这种方法就无法实现,因为这种方法不能根据时间来做限制。那可以优化吗?

优化

哈哈,当然是可以的,我们不对计数器设置最大值,额外设置一个监听器,这个监听器每分钟计算一次新增的请求数,如果超过限值就拒绝后面的请求。

方案缺陷思考

有没有发现这个方法有些问题?如果第一秒接受请求300次,第二秒接受请求300次,第三秒接受请求400次,那后面从第四秒到一分钟的请求就都被拒绝了,但其实网关还能处理能力,所以这种方法无法做到平滑限流。

令牌桶

由上面方案的思考要做到平滑限流,可以设计一个对象,这个对象可以匀速的生产令牌,每个请求在得到处理前都要先尝试获取一个令牌,如果获取失败,网关就拒绝服务。

根据这个思路,我们可以设计出如下图所示的方案:
API网关-限流

方案分析:

  • 根据不同的请求设置多个不同的桶,每个桶有一定的容量,容量大小关系不大。

  • 根据情况以不同的固定速率分别向对应的桶中添加令牌。每分钟向桶内添加令牌的速率就是对相应类型请求的流量限制。

  • 桶满时丢弃新增的令牌。

  • 如果发现桶经常满,说明令牌补充的过快,超过了请求进来的速率,这时就失去了限流的效果了,可以考虑减慢往桶里添加令牌的速率。

  • 每次请求消耗一个令牌,也可根据数据包大小来消耗对应的令牌数。

  • 当令牌不足时, 网关拒绝请求。

这种方案不仅可以平滑限流,而且可以通过监控桶内令牌数的多少来判断处理请求的能力,从而针对性的增强特定请求处理能力。

漏桶

漏桶算法和令牌桶算法原理类似,利用底部破洞的桶,请求可以匀速流出,如下图:
API网关-限流

方案分析

  • 根据不同的请求设置多个不同的漏桶,每个桶有一定的容量。
  • 按照固定速率流出请求。
  • 流入请求的速率固定,溢出则被丢弃。
  • 如果发现桶经常满,说明令牌流出速率过慢,可以考虑加快请求流出速率。

与令牌桶不一样的是, 漏桶算法中消费请求是匀速的,可以用来进行流量整形。

分布式限流

为了实现全局限流,我们需要把令牌桶或者漏桶设置在共享介质中,比如Redis。此外对于桶的读写必须是源自操作。

何时限流

限流和登录认证以及权限控制共同组成API网关的防御大闸,这里面有个问题,这三者的顺序应该怎么排呢?

如果登录认证服务和权限控制服务能扛得住所有请求的压力的话,那么你怎么排都没有问题,但是如果请求量会非常大的话,建议还是把限流放在最外层,如果你不想自己实现限流功能的话,利用Nginx也可以完成想要的功能,具体在Nginx中如何配置这里不做进一步讲解。

相关文章: