java 限流(流量削峰)的几种方式和学习

一、为什么要限流,怎么限流

由于互联网公司的流量巨大,系统上线会做一个流量峰值的评估,尤其是像各种秒杀促销活动,为了保证系统不被巨大的流量压垮,会在系统流量到达一定阈值时,拒绝掉一部分流量。
限流会导致用户在短时间内(这个时间段是毫秒级的)系统不可用,一般我们衡量系统处理能力的指标是每秒的QPS或者TPS,假设系统每秒的流量阈值是1000,理论上一秒内有第1001个请求进来时,那么这个请求就会被限流。

感觉限流得分情况:比如限定所有请求1s内最多不能访问1000次,那么若有一个用户端捣乱,他一个人1s内就访问了999次,那其他的客户还有机会访问吗?所以我觉的得考虑情况:

  1. 所有访问者 在一定时间 $time 内只能访问 $limitCount 次;
  2. 访问者 $ip  在一定时间 $time 内只能访问 $limitCount 次;

二、限流要考虑的问题

简单实现思路: [ n, n+100s ] 时间内限制最大访问 limitCount=1000, [ n+100s, n+200s ] 时间内限制最大访问 limitCount=1000  ... ...

设置问题: 一天24h,100s 生成创建一个时间区间窗口,那 24h 我得生成创建多少个窗口呀?这也太多了吧!

统计问题:若有需求,如何统计 [n+50, n+150s] 内的访问量?


三、常见的限流方案

3.1  令牌桶算法

令牌桶算法的原理也比较简单,我们可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。
系统会维护一个令牌(token)桶,
以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。令牌桶算法通过控制 桶的容量、发放令牌的速率,来达到对请求的限制。

3.2  漏桶算法

漏桶算法思路很简单,我们把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出;

当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。

感觉落脚点都在控制速率上,速率有: 静态的速率----往令牌桶里放的速率,动态速率----流入速率 减去 流出速率;但java有啥好的技术来构建速率吗?

3.3  redis + zset 限流

滑动窗口法,简单来说就是随着时间的推移,时间窗口也会持续移动,有一个计数器不断维护着窗口内的请求数量,这样就可以保证任意时间段内,都不会超过最大允许的请求数。例如当前时间窗口是0s~60s,请求数是40,10s后时间窗口就变成了10s~70s,请求数是60;

窗口的时间段更新是联动的,随着请求的发起而更新联动;

为啥用 redis 实现 滑动时间窗口?它有啥特性?

  1. 滑动窗口说白了就是时间边界都加上period生成新边界, redis可以轻而易举的实现该功能;
  2. redis 的 zset 数据结构提供的 range 方法 让我们可以很轻易的获取到2个时间戳内有多少请求,构建时间区间;

请求过程: 

我要求1分钟内最多有5个请求可以访问
03:10:01发起请求,redis生成 score=03:10:01 的member[ 03:09:01, 03:10:01 ] 时间段redis有1个值,1<5  , 不丢弃
03:10:02发起请求,redis生成 score=03:10:02 的member;[ 03:09:02, 03:10:02 ] 时间段redis有2个值,2<5  , 不丢弃
03:10:03发起请求,redis生成  score=03:10:03 的member[ 03:09:03, 03:10:03 ] 时间段redis有3个值,3<5  , 不丢弃
03:10:04发起请求,redis生成  score=03:10:04 的member[ 03:09:04, 03:10:04 ] 时间段redis有4个值,4<5  , 不丢弃
03:10:05发起请求,redis生成  score=03:10:05 的member[ 03:09:05, 03:10:05 ] 时间段redis有5个值,5=5  , 不丢弃
03:10:06发起请求,redis生成  score=03:10:06 的member[ 03:09:06, 03:10:06 ] 时间段redis有6个值,6>5  , 丢弃
03:10:07发起请求,redis生成  score=03:10:07 的member[ 03:09:07, 03:10:07 ] 时间段redis有7个值,7>5  , 丢弃
03:10:08发起请求,redis生成  score=03:10:08 的member[ 03:09:08, 03:10:08 ] 时间段redis有8个值,8>5  , 丢弃
03:10:09发起请求,redis生成  score=03:10:09 的member[ 03:09:09, 03:10:09 ] 时间段redis有9个值,9>5  , 丢弃

 

3.4  redis + Lua限流

首先:Lua是个什么东西?

他是一种C语言编写的脚本,Lua脚本和 MySQL数据库的存储过程比较相似,他们执行一组命令,所有命令的执行要么全部成功或者失败,以此达到原子性;它可以作为普通的配置文件,代替XML,ini等文件格式;

redis为什么要用Lua?

虽然redis 官方没有直接提供限流相应的API,但却支持了 Lua 脚本的功能,可以使用它实现复杂的令牌桶或漏桶算法,也是分布式系统中实现限流的主要方式之一;

相比Redis事务,Lua脚本的优点:

  • 减少网络开销:使用Lua脚本,无需向Redis发送多次请求,执行一次即可,减少网络传输;
  • 原子操作:Redis将整个Lua脚本作为一个命令执行,原子,无需担心并发;
  • 复用:Lua脚本一旦执行,会永久保存 Redis 中,其他客户端可复用;

具体实现方式我就不整理了,可以扒原文链接 https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA

3.5  网关层限流

限流常在网关这一层做,比如Nginx、Openresty、Kong、Zuul、Spring Cloud Gateway等,而像spring cloud - gateway网关限流底层实现原理,就是基于Redis + Lua,通过内置Lua限流脚本的方式。

4.jpg


总结:

感觉还是 redis + zset 能消除我开头提出的顾虑,zset 真的很适合做限流;目前好像用的最多的也是redis进行限流;redis + zset + Lua 方案应该最优,但我没具体尝试;


原文链接 https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值