开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。

  • 缓存:缓存的目的是提升系统访问速度和增大系统处理容量。

  • 降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

  • 限流:限流的目的是通过对并发请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以进行拒绝服务、排队或等待、降级等处理。

限流是限制系统的输入和输出流量,以达到保护系统的目的,而限流的实现主要是依靠限流算法,限流算法主要有4种:

  1. 固定时间窗口算法(计数器)

  2. 滑动时间窗口算法

  3. 令牌桶算法

  4. 漏桶算法

1. 固定时间窗口算法

又称计数器算法。固定时间窗口算法就是统计记录单位时间内进入系统或者某一接口的请求次数,在限定的次数内的请求则正常接收处理,超过次数的请求则拒绝掉或者改为异步处理等限流措施。

时间窗口长度如果为1分钟,如图。

此算法在单机还是分布式环境下实现都非常简单,使用redis的incr原子自增性即可轻松实现。

单机伪代码如下。

class CounterDemo {public       long timeStamp = getNowTime();public       int  reqCount  = 0;public final int  limit     = 100; // 时间窗口内最大请求数public final long interval  = 1000; // 时间窗口mspublic boolean grant() {long now = getNowTime();if (now < timeStamp + interval) {// 在时间窗口内reqCount++;// 判断当前时间窗口内是否超过最大请求控制数return reqCount <= limit;} else {timeStamp = now;// 超时后重置reqCount = 1;return true;}}
}

算法特点

  1. 实现简单。

  2. 时间窗口固定,每个窗口开始时计数为零,这样后面的请求不会受到之前的影响,做到了前后请求隔离。

  3. 因为两个时间窗口之间没有任何联系,所以调用者可以在一个时间窗口的结束到下一个时间窗口的开始这个非常短的时间段内发起两倍于阈值的请求。所以固定时间窗口算法无法限制窗口间突发流量。

2. 滑动时间窗口算法

滑动时间窗口算法其实是固定时间窗口算法的优化,主要是为了解决固定时间窗口算法无法限制窗口间突发流量的缺点。
上面的计数器的单位时间是1分钟,而在使用滑动时间窗口,可以把1分钟分成6格,每格时间长度是10s,每一格又各自管理一个计数器,单位时间用一个长度为60s的窗口描述。一个请求进入系统,对应的时间格子的计数器便会+1,而每过10s,这个窗口便会向右滑动一格。只要窗口包括的所有格子的计数器总和超过限流上限,便会执行限流措施。

由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

算法特点

  1. 因为窗口顺延,所以可以抵御窗口间突发流量(对比固定时间窗口算法)。

  2. 假如限流10万次/小时,如果某个调用者在前10分钟调用了10万次那么他必须再等待1小时才能发起下一次正常请求。所以没有做到前后请求隔离。

阿里开源的Sentinel,采用的是滑动窗口算法进行限流,可以阅读相关代码,加深对滑动时间窗口算法的理解。

3. 漏桶算法(leaky bucket)

漏桶算法其实很简单,可以粗略的认为就是注水漏水过程,往桶中以一定速率流出水,以任意速率流入水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。这个从桶底流出去的水就是系统正常处理的请求,从旁边流出去的水就是系统拒绝掉的请求。

单机伪代码如下。

class LeakyDemo {public long timeStamp = getNowTime();public int capacity; // 桶的容量public int rate; // 水漏出的速度public int water; // 当前水量(当前累积请求数)public boolean grant() {long now = getNowTime();water = max(0, water - (now - timeStamp) * rate); // 先执行漏水,计算剩余水量timeStamp = now;if ((water + 1) < capacity) {// 尝试加水,并且水还未满water += 1;return true;} else {// 水满,拒绝加水return false;}}
}

算法特点

  1. 因为流出的速度是一定的,可以抵御突发流量,做到更加平滑的限流,而且不允许流量突发。

4. 令牌桶算法(Token Bucket)

令牌桶算法是比较常见的限流算法之一,Google开源项目Guava中的RateLimiter使用的就是令牌桶算法。流程如下:

  1. 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理。

  2. 根据限流大小,设置按照一定的速率往桶里添加令牌。

  3. 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝。

  4. 请求到达后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除。

单机伪代码如下,分布式环境可以使用Redisson。

class TokenBucketDemo {public long timeStamp = getNowTime();public int capacity; // 桶的容量public int rate; // 令牌放入速度public int tokens; // 当前令牌数量public boolean grant() {long now = getNowTime();// 先添加令牌tokens = min(capacity, tokens + (now - timeStamp) * rate);timeStamp = now;if (tokens < 1) {// 若桶中没有令牌,则拒绝return false;} else {// 还有令牌,领取令牌tokens -= 1;return true;}}
}

算法特点

  1. 可以抵御突发流量,因为桶内的令牌数不会超过给定的最大值

  2. 可以做到更加平滑的限流,因为令牌是匀速放入的。

  3. 令牌桶算法允许流量一定程度的突发。(相比漏桶算法)

在时间点刷新的临界点上,只要剩余token足够,令牌桶算法会允许对应数量的请求通过,而后刷新时间因为token不足,流量也会被限制在外,这样就比较好的控制了瞬时流量。因此,令牌桶算法也被广泛使用。

更多内容,欢迎关注微信公众号:全菜工程师小辉~

高并发系统的限流算法与实现相关推荐

  1. 谈谈高并发系统的限流

    开涛大神在博客中说过:在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流.本文结合作者的一些经验介绍限流的相关概念.算法和常规的实现方式. 缓存 缓存比较好理解,在大型高并发系统中,如果没有缓 ...

  2. 高并发系统之限流特技

    转载至:http://blog.csdn.net/g_hongjin/article/details/51649246 在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流.缓存的目的是提升系统 ...

  3. Java高并发系统的限流策略

    限流算法 令牌桶(Token Bucket).漏桶(leaky bucket)和计数器算法是最常用的三种限流的算法. 计数器限流算法也是比较常用的,主要用来限制总并发数,比如数据库连接池大小.线程池大 ...

  4. 慌了,居然被问到怎么做高并发系统的限流

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"加入公众号专属技术群 来源:uee.me/cDuRD 在开发高并发系统时有三把利 ...

  5. 面试官 | 讲一下如何给高并发系统做限流?

    作者 | nick hao 来源 | uee.me/cDuRD 在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流.本文结合作者的一些经验介绍限流的相关概念.算法和常规的实现方式. 缓存 缓存 ...

  6. 聊聊高并发系统之限流特技-1

    在开发高并发系统时有三把利器用来保护系统:缓存.降级和限流. 缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹:而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉 ...

  7. 高并发中的 限流、熔断、降级、预热、背压你都知道是什么意思吗?

    首先,我们需要明确一下这几个名词出现的场景:分布式高并发环境.如果你的产品卖相不好,没人鸟它,那它就用不着这几个属性.不需要任何加成,低并发系统就能工作的很好. 分布式系统是一个整体,调用关系错综复杂 ...

  8. 高并发策略之限流:计数器、漏桶、令牌桶 三大算法的原理与实战(史上最全)

    导读 网站高可用指的就是:在绝大多的时间里,网站一直处于可以对外提供服务的正常状态. 一般以"年"为单位来统计,"9"的个数越多,代表一年中允许的不可用时间就越 ...

  9. 高并发系统限流-漏桶算法和令牌桶算法

    参考: https://www.cnblogs.com/xuwc/p/9123078.html http://www.cnblogs.com/LBSer/p/4083131.html https:// ...

最新文章

  1. RS-485知识点小结
  2. matlab写字,Matlab实现鼠标写字代码
  3. OpenShift 4 - Pod的亲和性/反亲和性
  4. 完美解决github访问速度慢
  5. 中文谐音怎么读_日语零基础学习,谐音法巧记日语50音图发音
  6. Android ViewModel
  7. php--学习封装类 (一)(操作mysql数据库的数据访问)
  8. 与时间有关的10个短语
  9. 计算机找不到网络链接,找不到宽带连接的解决办法-电脑故障
  10. webrtc 研究-带宽控制
  11. 从零开始写一个Jison解析器(2/10):学习解析器生成器parser generator的正确姿势
  12. 21种低成本的方式,帮助企业获得关注
  13. 剑指offer-面试题23:链表中环的入口节点 快慢指针+双指针
  14. 数的大家庭——虚数和复数的由来
  15. Python实现PPT转化为Word和OCR识别
  16. 刺激汽车消费政策频出的背后揭露了什么样的车市真相?
  17. C++入门:鸡兔同笼问题
  18. 微型计算机的硬件结构采用,微型计算机硬件结构内部结构
  19. python美观代码_为什么Python 代码要写得美观而明确
  20. java calendar 2月份_Calendar.MONTH 2月29号加一个月 问题

热门文章

  1. [禅悟人生]疑问是成长的标志
  2. Thinkpad E450c 系统起不来解决方案
  3. 机械螺旋缠绕法管道非开挖修复
  4. Java序列中如果有些字段不想被序列化,怎么办
  5. 用JS实现猜数字游戏
  6. C# 窗体视频控件进入全屏模式和退出全屏模式
  7. 做好PMC管理三大工作,轻松搞定生产计划与物料控制
  8. fx3g485通讯模块_三菱模块FX3U-485ADP-MB MODBUS通信模块
  9. 计算机毕业设计之android的二手车交易系统app(源码+系统+mysql数据库+Lw文档)
  10. inventor如何画心_Illustrator | 如何画一个心型图案