10-Sentinel与Gateway的限流对比,滑动窗口、令牌桶、漏桶_哔哩哔哩_bilibili
拒绝宕机!一文详解分布式限流方案(附代码实现) (很详细的文章,有代码实现)
Redis--Zset使用场景举例(滑动窗口实现限流)_redis 滑动窗口-CSDN博客
【Redis Lua脚本 分布式限流 窗口 令牌桶 漏桶】_redis 令牌桶-CSDN博客
滑动窗口
使用Redis 中 zset数据类型。
滑动窗口是一种流量控制策略,用于控制一定时间内请求的访问数量。
其原理是:将时间划分成规定的时间片段,每个片段有固定的时间间隔,如1s,1min,1h,然后定义一个时间窗口,比如5s,5min等,该窗口会随着时间向右移动。此外还需要计数器计算窗口内的请求数。当窗口移动时,会把已经走过的时间片段的请求数删掉。每当请求进入系统时,会检查计数器中的请求数是否已经满了,如果计数未满,则请求允许被执行;否则执行相应的拒绝方法。
例如 60s 内 限制100个请求,那么窗口大小为60
向zset添加数据:zadd key score member 将 score设置为 请求时间戳 ,member保证唯一性
删除zset某个score范围内的数据: zremrangebyscore key min max min设置为【无穷小】 ,max为 【当前时间戳 - 窗口大小】
统计zset中数据的数量 : zcard key 这个数量就是窗口内请求数量。
具体步骤
最好用lua脚本
- 计算删除范围的max值,删除 窗口 外的数据。
- 统计zset中数量 判断是否 超过阈值。
- 超过,不允许请求。没超过,允许请求并向zset中添加数据
zadd key timestamp member
总结:先删,再统计,最后再加。
漏桶
漏桶算法的原理就是 将请求加入漏桶中,漏桶以固定速率出水,如果请求在漏桶中溢出就拒绝请求
那么这个漏桶就可以使用一定长度的队列来实现,长度就是这个漏桶所能容纳请求的数量,再通过另一个线程从队列的另一端去不断取出任务执行就可以了
漏桶的问题
漏桶算法存在的问题就是只能以固定速率处理到来的请求,无法处理突发请求 ,也就是一瞬间如果有超过漏桶大小的请求数量过来的话,超出的那部分请求就会被无情的抛弃
那么漏桶算法的这个问题在令牌桶算法中得到了解决 ,如果请求一开始数量较少,令牌桶中会积累令牌数量,当有突发流量到来的时候,会去使用已经积累的令牌数量来去处理这些请求。并且 RateLimiter 的实现中 还可以对未来令牌数量透支 ,这样 RateLimiter 实现的令牌桶算法就可以很好的应对突发流量了,不过这样带来的缺点就是如果一直并发量比较高,导致对未来的令牌数量一直透支,会导致后边请求的阻塞等待时间逐渐变长,解决方法就是适当的加一些请求拒绝策略就可以缓解这种现象
漏桶算法和令牌桶算法还有一点区别就是:
漏桶算法是需要将请求给存储在队列中,而在令牌桶算法中,并没有真正去产生令牌,而是根据时间差来计算这段时间应该产生的令牌数, 所以令牌桶算法的性能相对于漏桶算法来说是比较高的!
令牌桶
令牌桶算法的原理就是 系统使用恒定速率往桶中放入令牌,如果请求需要被处理,就从桶中获取令牌,如果没有令牌的话,请求被拒绝
RateLimiter 就是基于令牌桶算法实现的,在他里边并没有真正的去创建令牌实体,而是根据时间差来计算这一段时间产生的令牌数,这样做的好处就是 性能比较高
令牌桶和漏桶的比较
漏桶算法 :主要用于平滑流量,对于突发流量的应对不好,如果突发流量过大超出了队列长度就会被无情抛弃;并且需要将请求存储在队列中
令牌桶算法 :
- 为了更好应对突发流量引入了透支令牌的优化,但是如果一直透支对后来的请求也很不友好,有利有弊;
- 并且
RateLimiter中对令牌桶算法还做出了优化,并不真正去生成令牌实体,而是根据时间去计算应该生成的令牌数,降低系统开销
RateLimiter 的guava 自带工具类,适用于单机限流,支持令牌桶限流。
单机限流与分布式限流
单机限流
单机限流针对的是单体架构应用。
单机限流可以直接使用 Google Guava 自带的限流工具类 RateLimiter 。 RateLimiter 基于令牌桶算法,可以应对突发流量。
Guava 地址:github.com/google/guav…
除了最基本的令牌桶算法(平滑突发限流)实现之外,Guava 的RateLimiter还提供了 平滑预热限流 的算法实现。
平滑突发限流就是按照指定的速率放令牌到桶里,而平滑预热限流会有一段预热时间,预热时间之内,速率会逐渐提升到配置的速率。
分布式限流
分布式限流针对的分布式/微服务应用架构应用,在这种架构下,单机限流就不适用了,因为会存在多种服务,并且一种服务也可能会被部署多份。
分布式限流常见的方案:
- 借助中间件架限流:可以借助 Sentinel 或者使用 Redis 来自己实现对应的限流逻辑。
- 网关层限流:比较常用的一种方案,直接在网关层把限流给安排上了。不过,通常网关层限流通常也需要借助到中间件/框架。就比如 Spring Cloud Gateway 的分布式限流实现RedisRateLimiter就是基于 Redis+Lua 来实现的,再比如 Spring Cloud Gateway 还可以整合 Sentinel 来做限流。
如果你要基于 Redis 来手动实现限流逻辑的话,建议配合 Lua 脚本来做。
网上也有很多现成的优秀的限流脚本供你参考,就比如 Apache 网关项目 ShenYu 的 RateLimiter 限流插件就基于 Redis + Lua 实现了 令牌桶算法/并发令牌桶算法、漏桶算法、滑动窗口 算法。
ShenYu 地址: github.com/apache/incu…
另外,如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的 RRateLimiter 来实现分布式限流,其底层实现就是基于 Lua 代码+令牌桶算法。