常用限流算法分析

一、计数器(固定窗口)算法

计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。

此算法在单机还是分布式环境下实现都非常简单,使用redis的incr原子自增性和线程安全即可轻松实现。
在这里插入图片描述
计数器算法对于秒级以上的时间周期来说,会存在一个非常严重的问题,那就是临界问题,如下图:
在这里插入图片描述
假设1min内服务器的负载能力为100,因此一个周期的访问量限制在100,然而在第一个周期的最后5秒和下一个周期的开始5秒时间段内,分别涌入100的访问量,虽然没有超过每个周期的限制量,但是整体上10秒内已达到200的访问量,已远远超过服务器的负载能力,由此可见,计数器算法方式限流对于周期比较长的限流,存在很大的弊端。

二、滑动窗口

滑动窗口算法是将时间周期分为N个小周期,每个小周期分别记录访问次数,并且根据时间滑动删除过期的小周期,添加新的小周期。小周期的访问次数和的最大值等于限流值。
在这里插入图片描述

假设1分钟允许100个请求,然后我们将时间窗口进行划分,比如图中,我们就将滑动窗口划成了6格,所以每格代表的是10秒钟。每过10秒钟,我们的时间窗口就会往右滑动一格。每一个格子都有自己独立的计数器counter,比如当一个请求 在0:35秒的时候到达,那么0:30~0:39对应的counter就会加1。

滑动窗口如何解决临界问题:
当最后0:59和1:01都来了100个请求,在0:59所属的周期没有过期前,因为已经达到限流值,因此会出发限流,直到0:59所属的周期过期,才能接受新的请求,解决了临界问题。

回顾一下上面的计数器算法,我们可以发现,计数器算法其实就是滑动窗口算法。只是它没有对时间窗口做进一步地划分,所以只有1格。
由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确

滑动窗口的缺点

滑动窗口的实现:简单的java实现滑动时间窗口限流算法

单机下可以通过队列实现,分布式下可以通过Redis的 zset 实现。滑动窗口需要记录每个请求的时间戳,因此内存消耗比较大,占用存储空间高,且不能保证请求稳定。

三、漏桶算法

漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率,不允许突发流量
在这里插入图片描述
对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发流量。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。

四、令牌桶

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
在这里插入图片描述

漏桶法和令牌桶的区别

两者主要区别在

  • 漏桶算法能够强行限制数据的传输速率
  • 令牌桶算法在能够限制数据的平均传输速率外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,所以它适合于具有突发特性的流量

令牌桶可以用来保护自己,主要用来对调用者频率进行限流,为的是让自己不被打垮。所以如果自己本身有处理能力的时候,如果流量突发(实际消费能力强于配置的流量限制),那么实际处理速率可以超过配置的限制。

漏桶算法,这是用来保护他人,也就是保护他所调用的系统。主要场景是,当调用的第三方系统本身没有保护机制,或者有流量限制的时候,我们的调用速度不能超过他的限制,由于我们不能更改第三方系统,所以只有在主调方控制。这个时候,即使流量突发,也必须舍弃。因为消费能力是第三方决定的。

总结:如果要让自己的系统不被打垮,用令牌桶。如果保证别人的系统不被打垮,用漏桶算法

小结

从上面看来好像漏桶和令牌桶比时间窗口算法好多了,那时间窗口算法有啥子用,扔了扔了?

并不是的,虽然漏桶和令牌桶对比时间窗口对流量的整形效果更佳,流量更加得平滑,但是也有各自的缺点(上面已经提到了一部分)。

拿令牌桶来说,假设你没预热,那是不是上线时候桶里没令牌?没令牌请求过来不就直接拒了么?这就误杀了,明明系统没啥负载现在。

再比如说请求的访问其实是随机的,假设令牌桶每20ms放入一个令牌,桶内初始没令牌,这请求就刚好在第一个20ms内有两个请求,再过20ms里面没请求,其实从40ms来看只有2个请求,应该都放行的,而有一个请求就直接被拒了。这就有可能造成很多请求的误杀,但是如果看监控曲线的话,好像流量很平滑,峰值也控制的很好。

再拿漏桶来说,漏桶中请求是暂时存在桶内的。这其实不符合互联网业务低延迟的要求

所以漏桶和令牌桶其实比较适合阻塞式限流场景,即没令牌我就等着,这就不会误杀了,而漏桶本就是等着。比较适合后台任务类的限流。而基于时间窗口的限流比较适合对时间敏感的场景,请求过不了您就快点儿告诉我,等的花儿都谢了。

单机限流和分布式限流

本质上单机限流和分布式限流的区别其实就在于 “阈值” 、窗口、漏斗存放的位置

单机限流就上面所说的算法直接在单台服务器上实现就好了,而往往我们的服务是集群部署的。因此需要多台机器协同提供限流功能。

像上述的计数器或者时间窗口的算法,可以将计数器存放至 Tair 或 Redis 等分布式 K-V 存储中。

例如滑动窗口的每个请求的时间记录可以利用 Redis 的 zset 存储,利用ZREMRANGEBYSCORE 删除时间窗口之外的数据,再用 ZCARD计数。

像令牌桶也可以将令牌数量放到 Redis 中。

不过这样的方式等于每一个请求我们都需要去Redis判断一下能不能通过,在性能上有一定的损耗,所以有个优化点就是 「批量」。例如每次取令牌不是一个一取,而是取一批,不够了再去取一批。这样可以减少对 Redis 的请求。

不过要注意一点,批量获取会导致一定范围内的限流误差。比如你取了 10 个此时不用,等下一秒再用,那同一时刻集群机器总处理量可能会超过阈值。

其实「批量」这个优化点太常见了,不论是 MySQL 的批量刷盘,还是 Kafka 消息的批量发送还是分布式 ID 的高性能发号,都包含了「批量」的思想。

当然分布式限流还有一种思想是平分,假设之前单机限流 500,现在集群部署了 5 台,那就让每台继续限流 500 呗,即在总的入口做总的限流限制,然后每台机子再自己实现限流。

总结

(1)计数器限流算法:简单粗暴但边界值统计不准确(临界问题

(2)滑动窗口限流算法:统计准确易理解,占用存储空间高,且不能保证请求稳定,适合时间敏感的场景

(3)令牌桶限流算法:相比滑动窗口限流算法占用空间少,可以限制平均流量和流量最大值,适合有突发流量需求的场景。存在误杀的问题,需要预热(启动时先放入一些令牌)。

(4)漏斗限流算法:相比滑动窗口限流算法占用空间少,可以严格限制流量请求会先放在桶中,不适合低延迟的业务

参考

  • 重点好文:图解+代码|常见限流算法以及限流在单机分布式场景下的思考
  • 推荐好文:高并发系统限流-漏桶算法和令牌桶算法
  • 好文:大话常用限流算法与应用场景
  • 接口限流算法:漏桶算法&令牌桶算法
  • 精度不够,滑动时间来凑「限流算法第二把法器:滑动时间窗口算法」- 第301篇
  • 限流的算法有哪些?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330029.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx学习小结

nginx 【0】README 本文po处理 nginx的主要内容,包括反向代理,负载均衡,动静分离,高可用集群等; 本文引用链接: vmware安装centos8,refer2 https://blog.csdn.net/PacosonSWJTU/article/detail…

缓存与数据库的一致性:先操作缓存还是先操作数据库?

数据缓存 在我们实际的业务场景中,一定有很多需要做数据缓存的场景,比如售卖商品的页面,包括了许多并发访问量很大的数据,它们可以称作是是“热点”数据,这些数据有一个特点,就是更新频率低,读…

Object.hashCode()与Object.equals()

【README】 本文旨在po出 hashCode , equals的api描述,以加深理解; 本文翻译自 jdk 文档; 【1】Object.hashCode() 1)介绍:返回对象的哈希码值。支持此方法是为了有利于哈希表,例如由 java.u…

for in for of区别_(for…in) VS (for…of)

这篇文章应该是在一年多之前读过的,那会看完感觉作者文采不错,就做了收藏,做此分享,希望能帮助到你更好的理解js中的循环~~~以下正文。。。今天可是个好日子!你问我为什么?你这都不知道,ChinaJo…

Innodb中的事务隔离级别和锁的关系

前言 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式。同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力…

并发队列-无界非阻塞队列 ConcurrentLinkedQueue 原理探究

转载自 并发队列-无界非阻塞队列 ConcurrentLinkedQueue 原理探究一、 前言 常用的并发队列有阻塞队列和非阻塞队列,前者使用锁实现,后者则使用CAS非阻塞算法实现,使用非阻塞队列一般性能比较好,下面就看看常用的非阻塞Concurrent…

(转)如何保障微服务架构下的数据一致性?

转自: https://cloud.tencent.com/developer/article/1459734 【1】写在前面 随着微服务架构的推广,越来越多的公司采用微服务架构来构建自己的业务平台。就像前边的文章说的,微服务架构为业务开发带来了诸多好处的同时,例如单一…

python中math库_Python的math库、random库实际应用

昨天在说那个列表的时候,我挖了一个坑,不知道你们看出来没有?就是用循环语句写迭代的时候,总是运行不了结果,其实是因为我没有缩进的问题,因为有一个for循环,下面print如果没有对应的缩进&#…

(转)漫画:什么是分布式事务?

转自: https://blog.csdn.net/bjweimengshu/article/details/79607522 假如没有分布式事务 在一系列微服务系统当中,假如不存在分布式事务,会发生什么呢?让我们以互联网中常用的交易业务为例子: 上图中包含了库存和订…

Java 线程池框架核心代码分析

转载自 Java 线程池框架核心代码分析前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的。线程池应运而生,成为我们管理线程的利器。Java 通过Executor接口,提供了一种标准的方法将任务的提…

python渐变色代码_如何在Python中创建颜色渐变?

6 个答案: 答案 0 :(得分:54) 我还没有看到一个简单的答案就是使用colour package。 通过pip安装 pip install colour 如此使用: from colour import Color red Color("red") colors list(red.range_to(Color("green"),10)) # col…

(转)web.xml 中的listener、 filter、servlet 加载顺序及其详解

转&#xff1a; https://www.cnblogs.com/Jeely/p/10762152.html web.xml 中的listener、 filter、servlet 加载顺序及其详解 一、概述 1、启动一个WEB项目的时候&#xff0c;WEB容器会去读取它的配置文件web.xml&#xff0c;读取<listener>和<context-param>两个…

柱状图python_python柱状图一行

编写计算柱状图的python程序有很多种方法。 通过柱状图,我指的是一个计算对象在 iterable 并在字典中输出计数。例如: >>> L abracadabra >>> histogram(L) {a: 5, b: 2, c: 1, d: 1, r: 2} 编写此函数的一种方法是: def histogram(L): d {} for x in L: i…

HashMap 和 HashTable 到底哪不同 ?

转载自 HashMap 和 HashTable 到底哪不同 &#xff1f;代码版本 JDK每一版本都在改进。本文讨论的HashMap和HashTable基于JDK 1.7.0_67。源码见这里 1. 时间 HashTable产生于JDK 1.1&#xff0c;而HashMap产生于JDK 1.2。从时间的维度上来看&#xff0c;HashMap要比HashTable出…

架构师成长之路(内附推荐书籍)

转&#xff1a; https://www.jianshu.com/p/f661f098b88a 想要成为架构师&#xff0c;对技术的深度和广度都有很高的要求&#xff0c;本文列举出成为一个架构师必备的技能和学习路线。 对于学习途径有疑惑或苦恼&#xff0c;或者有优秀资料可以提供的同学&#xff0c;可加留言&…

python 虚拟环境_理解Python虚拟环境

什么是环境既然有所谓的 虚拟环境&#xff08;Virtual Environment&#xff09;&#xff0c;那么首先有必要解释一下&#xff0c;什么是环境。这里的环境&#xff0c;指的就是 Python 代码的运行环境。它应该包含以下信息&#xff1a;Python 解释器&#xff0c;用哪个解释器来执…

java.util.concurrent.locks.Lock文档说明

【1】Lock接口文档描述 1.相比于使用synchronized方法和代码块&#xff0c;锁的出现提供了更广泛的锁操作。 锁允许更灵活的代码结构&#xff0c;具有许多不同的属性&#xff0c;还支持多个关联的Condition条件对象。 2.锁是用于控制多个线程访问共享资源的工具。通常&#…

20 个使用 Java CompletableFuture的例子

转载自 20 个使用 Java CompletableFuture的例子这篇文章介绍 Java 8 的 CompletionStage API和它的标准库的实现 CompletableFuture。API通过例子的方式演示了它的行为&#xff0c;每个例子演示一到两个行为。既然CompletableFuture类实现了CompletionStage接口&#xff0c;首…

fastreport 打印两个list_Smaller And Smarter Python数据结构:合并两个有序链表

原创&#xff1a; 老表 简说Python 今日问题 &#xff1a;翻转链表k个相邻结点"""目标&#xff1a;写一段程序&#xff0c;合并两个有序链表例如&#xff1a;输入-> 1->2->3输入-> 2->5->6->8输出-> 1->2->2->3->5->6-&…