没有合适的资源?快使用搜索试试~ 我知道了~
资源详情
资源评论
资源推荐
分布式环境下限流⽅案的实现redis
RateLimiter Guava,Token Bucket,
Leaky Bucket
业务背景介绍
对于web应⽤的限流,光看标题,似乎过于抽象,难以理解,那我们还
是以具体的某⼀个应⽤场景来引⼊这个话题吧。
在⽇常⽣活中,我们肯定收到过不少不少这样的短信,“双11约吗?,
千款….”,“您有幸获得唱读卡,赶快戳链接…”。这种类型的短信是属
于推⼴性质的短信。为什么我要说这个呢?听我慢慢道来。
⼀般⽽⾔,对于推⼴营销类短信,它们针对某⼀群体(譬如注册会员)进
⾏定点推送,有时这个群体的成员量⽐较⼤,譬如京东的会员,可以达
到千万级别。因此相应的,发送推⼴短信的量也会增⼤。然⽽,要完成
这些短信发送,我们是需要调⽤服务商的接⼜来完成的。倘若⼀次发送
的量在200万条,⽽我们的服务商接⼜每秒能处理的短信发送量有限,
只能达到200条每秒。那么这个时候就会产⽣问题了,我们如何能控制
好程序发送短信时的速度昵?于是限流这个功能就得加上了
⽣产环境背景
1、服务商接⼜所能提供的服务上限是400条/s
2、业务⽅调⽤短信发送接⼜的速度未知,QPS可能达到800/s,
1200/s,或者更⾼
3、当服务商接⼜访问频率超过400/s时,超过的量将拒绝服务,多出的
信息将会丢失
4、线上为多节点布置,但调⽤的是同⼀个服务商接⼜
需求分析
1、鉴于业务⽅对短信发送接⼜的调⽤频率未知,⽽服务商的接⼜服务
有上限,为保证服务的可⽤性,业务层需要对接⼜调⽤⽅的流量进⾏限
制—–接⼜限流
需求设计
⽅案⼀、在提供给业务⽅的Controller层进⾏控制。
1、使⽤guava提供⼯具库⾥的RateLimiter类(内部采⽤令牌捅算法实现)
进⾏限流
<!--核代码段-->
private RateLimiter rateLimiter = RateLimiter.create(400);//400表示每秒允许处
的是400
if(rateLimiter.tryAcquire()) {
//短信发送逻辑可以在此处
}
2、使⽤Java⾃带delayqueue的延迟队列实现(编码过程相对⿇烦,此处省略代
码)
3、使⽤Redis实现,存储两个key,⼀个⽤于计时,⼀个⽤于计数。请求每调
⽤⼀次,计数器增加1,若在计时器时间内计数器未超过阈值,则可以处理
任务
if(!cacheDao.hasKey(API_WEB_TIME_KEY)) {
cacheDao.putToValue(API_WEB_TIME_KEY,0,(long)1, TimeUnit.SECONDS);
}
if(cacheDao.hasKey(API_WEB_TIME_KEY)&&cacheDao.incrBy(API_WEB_COUNTER_KEY,
(long)1) > (long)400) {
LOGGER.info("调频率过快");
}
//短信发送逻辑
⽅案⼆、在短信发送⾄服务商时做限流处理
⽅案三、同时使⽤⽅案⼀和⽅案⼆
可⾏性分析
最快捷且有效的⽅式是使⽤RateLimiter实现,但是这很容易踩到⼀个
坑,单节点模式下,使⽤RateLimiter进⾏限流⼀点问题都没有。但是…
线上是分布式系统,布署了多个节点,⽽且多个节点最终调⽤的是同⼀
个短信服务商接⼜。虽然我们对单个节点能做到将QPS限制在400/s,
但是多节点条件下,如果每个节点均是400/s,那么到服务商那边的总
请求就是
节点
数x400/s,于是限流效果失效。使⽤该⽅案对单节点的阈
值控制是难以适应分布式环境的,⾄少⽬前我还没想到更为合适的⽅
式。
对于第⼆种,使⽤delayqueue⽅式。其实主要存在两个问题,1:短信系
统本⾝就⽤了⼀层消息队列,有⽤kafka,或者rabitmq,如果再加⼀层
延迟队列,从设计上来说是不太合适的。2:实现delayqueue的过程相对
较⿇烦,耗时可能⽐较长,⽽且达不到精准限流的效果
对于第三种,使⽤redis进⾏限流,其很好地解决了分布式环境下多实例
所导致的并发问题。因为使⽤redis设置的计时器和计数器均是全局唯⼀
的,不管多少个节点,它们使⽤的都是同样的计时器和计数器,因此可
以做到⾮常精准的流控。同时,这种⽅案编码并不复杂,可能需要的代
码不超过10⾏。
实施⽅案
根据可⾏性分析可知,整个系统采取redis限流处理是成本最低且最⾼效
的。
具体实现
1、在Controller层设置两个全局key,⼀个⽤于计数,另⼀个⽤于计时
private static final String API_WEB_TIME_KEY = "time_key";
private static final String API_WEB_COUNTER_KEY = "counter_key";
2、对时间key的存在与否进⾏判断,并对计数器是否超过阈值进⾏判断
if(!cacheDao.hasKey(API_WEB_TIME_KEY)) {
cacheDao.putToValue(API_WEB_TIME_KEY,0,(long)1,
TimeUnit.SECONDS);
cacheDao.putToValue(API_WEB_COUNTER_KEY,0,(long)2,
TimeUnit.SECONDS);//时间到就重新初始化为
}
if(cacheDao.hasKey(API_WEB_TIME_KEY)&&cacheDao.incrBy(API_WEB_COUNTER_KEY,
(long)1) > (long)400) {
LOGGER.info("调频率过快");
}
//短信发送逻辑
实施结果
可以达到⾮常精准的流控,截图会在后续的过程中贴出来。欢迎有疑问的⼩
伙伴们在评论区提出问题,我看到后尽量抽时间回答的
http://blog.csdn.net/Justnow_/article/details/53055299
⼀、场景描述
很多做服务接⼜的⼈或多或少的遇到这样的场景,由于业务应⽤系统的
负载能⼒有限,为了防⽌⾮预期的请求对系统压⼒过⼤⽽拖垮业务应⽤系
统。
也就是⾯对⼤流量时,如何进⾏流量控制?
服务接⼜的流量控制策略:分流、降级、限流等。本⽂讨论下限流策略,
虽然降低了服务接⼜的访问频率和并发量,却换取服务接⼜和业务应⽤系统
的⾼可⽤。
实际场景中常⽤的限流策略:
Nginx前端限流
按照⼀定的规则如帐号、IP、系统调⽤逻辑等在Nginx层⾯做限流
业务应⽤系统限流
1、客户端限流
2、服务端限流
数据库限流
红线区,⼒保数据库
⼆、常⽤的限流算法
常⽤的限流算法由:楼桶算法和令牌桶算法。本⽂不具体的详细说明两
种算法的原理,原理会在接下来的⽂章中做说明。
1、漏桶算法
漏桶(Leaky Bucket)算法思路很简单,⽔(请求)先进⼊到漏桶⾥,漏桶以⼀
定的速度出⽔(接⼜有响应速率),当⽔流⼊速度过⼤会直接溢出(访问频率超过
接⼜响应速率),然后就拒绝请求,可以看出漏桶算法能强⾏限制数据的传输速
率.⽰意图如下:
可见这⾥有两个变量,⼀个是桶的⼤⼩,⽀持流量突发增多时可以存多少
的⽔(burst),另⼀个是⽔桶漏洞的⼤⼩(rate)。
因为漏桶的漏出速率是固定的参数,所以,即使⽹络中不存在资源冲突(没
有发⽣拥塞),漏桶算法也不能使流突发(burst)到端⼜速率.因此,漏桶算法对于
存在突发特性的流量来说缺乏效率.
2、令牌桶算法
令牌桶算法(Token Bucket)和 Leaky Bucket 效果⼀样但⽅向相反的算法,
更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则
间隔是10ms)往桶⾥加⼊Token(想象和漏洞漏⽔相反,有个⽔龙头在不断的加
⽔),如果桶已经满了就不再加了.新请求来临时,会各⾃拿⾛⼀个Token,如果没
有Token可拿了就阻塞或者拒绝服务.
剩余25页未读,继续阅读
牛站长
- 粉丝: 23
- 资源: 299
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0