高并发的场景下,不能不说的限流算法

简介: 高并发的场景下,不能不说的限流算法


先举个例子,说明为什么要做“限流”。

旅游景点通常都会有最大的接待量,不可能无限制的放游客进入,比如故宫每天只卖八万张票,超过八万的游客,无法买票进入,因为如果超过八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和心情,并且还会有安全隐患;

只卖 N 张票,这就是一种限流的手段。

软件架构中的限流

软件架构中的限流也是类似,也是当系统资源不够的时候,已经不足以应对大量的请求,为了保证服务还能够正常运行,那么按照规则,系统会把多余的请求直接拒绝掉,以达到限流的效果;

对外限流:用户过多,或因为某个活动或热点问题引发的访问量的增加;恶意攻击,或被爬虫抓取数据等等。不知道大家注意过没有,比如双11,刚过12点有些顾客的网页或APP会显示下单失败的提示,有些就是被限流掉了。

对内限流:大部分公司,系统和系统之间都会相互调用,假如 A 系统 被 X、Y、Z 三个系统调用,当 X 系统的访问量徒增,导致 A 系统被调的挂掉了,那么会导致 Y、Z 系统也无法正常使用(依赖于 A 系统)。

计数器法

计数器法的原理就是限制单位时间内的处理请求数不超过阈值。

比如一个接口一分钟可以处理 1000 次请求,那么可以设置一个计数器,当有一次请求过来,计数器就加 1,如果一分钟以内计数器超过了 1000,那么后面再过来的请求就不再处理;

但是这个方法的缺点也很明显,因为请求的访问不一定是很平稳的,如果 0:59 过来了 1000 个请求,1:01 已经是下一个窗口,又过来了 1000 个请求,但实际上三秒内来了 2000 个请求,已经超过我们的限流上限了;

计数器法

public class CounterTest {   public static void main(String[] args) {       long timeStamp = System.currentTimeMillis();       int timeCount = 1;       int reqCount = 0; //单位时间内的请求数量       int reqCountTotal = 0; //请求总数       final int limit = 10; // 时间窗口内最大请求数       final long interval = 1000; // 时间窗口ms       for(;;){        // 当前时间        long now = System.currentTimeMillis();        if (now < timeStamp + interval) {         //在之间窗口内         reqCount ++ ;         //没有超过单位时间的请求数量         if(reqCount < limit){          reqCountTotal ++ ;          System.out.println(timeCount + " : " + reqCountTotal);         }        }else {         //下一个时间窗口         timeStamp = now;         reqCount = 0 ;         timeCount ++ ;        }     }   }}复制代码

滑动窗口算法

还拿上面的例子,一分钟分 6 份,每份 10 秒;每过 10 秒钟,我们的时间窗口就会往右滑动一格,每个格子都有独立的计数器,我们每次都计算时间窗口内的数量,可以解决计数器法中的问题,而且当滑动窗口的格子越多,那么限流的统计就会越精确。

具体可以参考下图,看图比较清晰:

滑动窗口法

漏桶算法

这个算法也很简单,就是我们有一个固定容量的桶,有水流进来,也有水流出去,我们不需要控制流进来的速度,只需要控制流出去的速度,如果水流进来的太快,桶满了,多余的水会溢出去,并不会影响水流出去的速度。

漏桶算法

令牌桶算法

还是有一个桶,桶里面有 N 个令牌,所有的请求在处理之前都需要拿到一个可用的令牌才会被处理,如果桶里面没有令牌的话,则拒绝服务;令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌。

有人会把漏桶算法和令牌桶算法混淆,而从某种意义上讲,令牌桶算法确实是对漏桶算法的一种改进,两者的不同之处在于:漏桶算法能够强行限制数据的传输速率,而令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输;

也就是说,如果令牌桶中存在令牌,则允许发送流量;而如果令牌桶中不存在令牌,则不允许发送流量;放入令牌的动作是持续执行的,比如桶的大小为 100 ,当一直没有请求的时候,桶内的令牌数量始终保持在 100 ,当下一秒有大量请求过来的时候,可以瞬间将“攒下”的 100 个令牌处理掉,然后再按照恒定速度进行处理。

令牌桶算法

再安利一下 Google 开源的 guava 包,使用 RateLimiter 我们可以很轻松的创建一个令牌桶算法的限流器。

import java.util.concurrent.Executors;import com.google.common.util.concurrent.ListeningExecutorService;import com.google.common.util.concurrent.MoreExecutors;import com.google.common.util.concurrent.RateLimiter;public class RateLimiterTest {    public static void main(String[] args) {        testRateLimiter();    }    public static void testRateLimiter() {           ListeningExecutorService executorService = MoreExecutors                .listeningDecorator(Executors.newFixedThreadPool(5));        RateLimiter limiter = RateLimiter.create(4); // 每秒不超过4个任务被提交        int num = 0 ;        for (;;) {            limiter.acquire(); // 请求RateLimiter, 超过permits会被阻塞            num ++ ;            executorService.submit(new Task("is "+ num));        }    }}class Task implements Runnable{    String str;    public Task(String str){        this.str = str;    }    @Override    public void run() {        System.out.println("Task call execute.." + str);    }}

目录
相关文章
|
8天前
|
缓存 监控 Java
Java 线程池在高并发场景下有哪些优势和潜在问题?
Java 线程池在高并发场景下有哪些优势和潜在问题?
|
14天前
|
NoSQL Java Redis
京东双十一高并发场景下的分布式锁性能优化
【10月更文挑战第20天】在电商领域,尤其是像京东双十一这样的大促活动,系统需要处理极高的并发请求。这些请求往往涉及库存的查询和更新,如果处理不当,很容易出现库存超卖、数据不一致等问题。
37 1
|
27天前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
51 4
|
25天前
|
存储 算法 安全
SM2算法的应用场景有哪些?
【10月更文挑战第5天】SM2算法的应用场景有哪些?
47 1
|
29天前
|
Java Linux
【网络】高并发场景处理:线程池和IO多路复用
【网络】高并发场景处理:线程池和IO多路复用
38 2
|
11天前
|
监控 算法 数据挖掘
HyperLogLog算法有哪些应用场景呢
【10月更文挑战第19天】HyperLogLog算法有哪些应用场景呢
11 0
|
2月前
|
算法 计算机视觉 Python
Python并查集大揭秘:让你在算法界呼风唤雨,秒杀一切复杂场景!
在编程与算法的广袤天地中,总有一些工具如同神兵利器,能够助你一臂之力,在复杂的问题前游刃有余。今天,我们就来深入探讨这样一件神器——Python并查集(Union-Find),看看它是如何让你在算法界呼风唤雨,轻松应对各种复杂场景的。
58 2
|
23天前
|
Java Linux 应用服务中间件
【编程进阶知识】高并发场景下Bio与Nio的比较及原理示意图
本文介绍了在Linux系统上使用Tomcat部署Java应用程序时,BIO(阻塞I/O)和NIO(非阻塞I/O)在网络编程中的实现和性能差异。BIO采用传统的线程模型,每个连接请求都会创建一个新线程进行处理,导致在高并发场景下存在严重的性能瓶颈,如阻塞等待和线程创建开销大等问题。而NIO则通过事件驱动机制,利用事件注册、事件轮询器和事件通知,实现了更高效的连接管理和数据传输,避免了阻塞和多级数据复制,显著提升了系统的并发处理能力。
35 0
|
26天前
|
消息中间件 前端开发 Java
java高并发场景RabbitMQ的使用
java高并发场景RabbitMQ的使用
70 0
|
27天前
|
机器学习/深度学习 自然语言处理 算法
神经网络算法以及应用场景和基本语法
神经网络算法以及应用场景和基本语法
34 0