avatar

目录
分布式限流

限流算法

固定窗口

(1)划分时间为多个窗口:固定一个时间周期,如10秒或者30秒

(2)在每个窗口期内,每有一个请求,计数器加一

(3)如果计数器超过了限制数量,则本窗口内所有的请求都被丢弃

(4)下一个时间窗口时,计数器重置

滑动窗口

滑动窗口其实就是对固定窗口做了进一步的细分,将原先的粒度切的更细,比如1分钟的固定窗口切分为60个1秒的滑动窗口。然后统计的时间范围随着时间的推移同步后移。

img

同时,我们还可以得出一个结论是:如果固定窗口的「固定周期」已经很小了,那么使用滑动窗口的意义也就没有了。举个例子,现在的固定窗口周期已经是1秒了,再切分到毫秒级别能反而得不偿失,会带来巨大的性能和资源损耗。

漏桶

(1)将每个请求视为“水滴”放入漏桶进行存储

(2)漏桶以固定速率漏出水滴(处理请求)

(3)漏桶满了,多余的水滴就丢弃

img

简单说来就是:如果当前速率小于阈值则直接处理请求,否则不直接处理请求,进入缓冲区,并增加当前水位

漏桶算法多使用队列实现,服务的请求会存到队列中,服务的提供方则按照固定的速率从队列中取出请求并执行,过多的请求则放在队列中排队或直接拒绝。

漏桶算法的缺陷也很明显,当短时间内有大量的突发请求时,即便此时服务器没有任何负载,每个请求也都得在队列中等待一段时间才能被响应。

令牌桶

(1)令牌以固定速率生成

(2)生成的令牌放入令牌桶中存放,如果令牌桶满了则多余的令牌直接丢弃,当请求到达时,会尝试从令牌桶中取令牌,得到令牌的请求可以执行

(3)如果桶空了,则丢弃取令牌的请求

img

令牌桶的容量大小理论上就是程序需要支撑的最大并发数。令牌桶算法既能够将所有的请求平均分布到时间区间内,又能接受服务器能够承受范围内的突发请求,因此是目前使用较为广泛的一种限流算法。

对于令牌桶的代码实现,可以直接使用Guava包中的RateLimiter

分布式场景

单节点模式下,使用RateLimiter进行限流一点问题都没有。但线上是分布式系统,布署了多个节点,而且多个节点最终调用的是同一个API/服务商接口。虽然我们对单个节点能做到将QPS限制在N/s,但是多节点条件下,如果每个节点均是N/s,那么到服务商那边的总请求就是节点数乘以N/s,于是限流效果失效。使用该方案对单节点的阈值控制是难以适应分布式环境的。

我们来看一下最简单的流量模型:

img

用户的请求从网关转发到后台服务,后台服务承接流量,调用缓存获取数据,缓存中的数据和数据库交互。这个模型就像一个漏斗一样,流量自上而下递减。

解决方案一:网关限流

服务网关,作为整个分布式链路中的第一关卡,承接了所有用户的访问请求,所以从这里限流肯定是大头。目前主流的网关层有以软件为代表的Nginx,Spring Cloud中的Gateway和Zuul这类的组件,当然也有硬件的网关限流。

1.Nginx限流:思想就是漏桶算法,即能够强行保证请求实时处理的速度不会超过设置的阈值

解决方案二:服务限流Redis 的 RateLimiter

https://segmentfault.com/a/1190000012947169

@GetMapping("/")

public void index(HttpServletResponse response) throws IOException {

Jedis jedis = jedisPool.getResource();

String token = RedisRateLimiter.acquireTokenFromBucket(jedis, LIMIT, TIMEOUT);

if (token == null) {

response.sendError(500);

}else{

//TODO 你的业务逻辑

}

jedisPool.returnResource(jedis);

}
文章作者: 简凡丶
文章链接: http://yoursite.com/2020/01/15/7.%20%E5%88%86%E5%B8%83%E5%BC%8F/%E5%88%86%E5%B8%83%E5%BC%8F%E9%99%90%E6%B5%81/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 BestBear

评论