限流算法(令牌桶漏桶计数器)

2024-05-13 12:20

本文主要是介绍限流算法(令牌桶漏桶计数器),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       📝个人主页:五敷有你      
 🔥系列专栏:Spring
⛺️稳中求进,晒太阳

业务重的三种情况:突发流量、恶意流量、业务本身需要

限流:   是为了保护自身系统和下游系统不被高并发流量冲垮,导致系统雪崩。

保证系统在可用的情况下尽可能增加进入的请求,其余的请求在排队等待,或者返回友好提示。保证进入系统的用户可以友好使用。

令牌桶算法

令牌桶算法是一个设定的速率产生令牌(token) 并放入令牌通,每次用户请求都得申请令牌。如果令牌不足,则拒绝请求。

        令牌桶算法中新请求到来时会从桶中拿走一个令牌,如果桶内没有i令牌可拿,就拒绝服务。

        当然令牌的数量也是有上限的。令牌的数量与时间和发放速率强相关。时间流逝的时间越长,会不断往桶里加入越多的令牌,如果令牌发送的速度比申请速度快,令牌会放满令牌桶,直到令牌占满令牌桶

令牌桶的算法特点:

好处:可以方便地应对突发出口流量。

比如:可以改变令牌发放速度(需要后端系统能力的提升),算法能按照新的发送速率调大令牌的发放数量,使得出口突发流量能被处理。

令牌生成的速度固定,消费速度不固定。

代码简单实现:

package ratelimit;import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;public class TokenBucketLimiter {//桶的容量private static int capacity=100;//令牌生成速度 rate/sprivate static final int rate=50;//令牌数量private volatile static AtomicInteger tokens=new AtomicInteger(0);/*** 开启一个线程,按固定频率放入令牌桶固定个数*/public static void productTokens(){ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(1);scheduledExecutorService.scheduleAtFixedRate(()->{int allTokens = tokens.get()+rate;//设置当前的tokens数量tokens.set(allTokens);},1000,1000,TimeUnit.MILLISECONDS);}/*** true是被限流了** @param needCount* @return*/public static synchronized boolean limited(int needCount){if(tokens.get()<needCount){return true;}System.out.println("此时令牌桶中数量有: "+tokens.getAndDecrement());return false;}public static void main(String[] args) {//开启生产者任务productTokens();//定义一个原子类,AtomicInteger atomicInteger=new AtomicInteger(0);ExecutorService executorService=Executors.newFixedThreadPool(5);for(int i=0;i<10000;i++){executorService.submit(()->{try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}//当前线程的名称String taskName=Thread.currentThread().getName();boolean isLimit=limited(1);//true被限流了if(isLimit){System.out.println(taskName+"被限流了,累计限流次数: "+atomicInteger.incrementAndGet());}else {System.out.println(taskName+"请求被正常处理了");}});}}}

 

漏桶算法

漏桶(Leak Bucket) 算法限流的基本原理:

水(对应请求) 从进水口到漏桶里,漏桶以一定的速度出水(请求放行),当水流速度过大,桶内的总水量大于桶容量会直接溢出,请求拒绝。

大致的规则如下:

1)进水口(对应客户端请求) 以任意速率流入漏桶。

2)漏桶的容量是固定的,出水(放行)速率也是固定的。

3)漏桶容量是不变的,如果处理速度太慢,桶内水的容量就会超出桶的容量,则后面的水滴会标识请求拒绝。

流程:

水(请求)先进入桶中,漏桶按照一定的速率进行漏水,如果漏桶满了,那么水就会溢出(请求拒绝),可以看出来漏桶算法能强行限制数据的传输速率。

代码实现

package ratelimit;import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;public class LeakBucketLimiter {//漏桶的容量private static int capacity=10;//漏水的速度 rate/sprivate static final int leakRate=5;//桶中水量private volatile static AtomicInteger waterLeaf=new AtomicInteger(0);/*** 开启一个线程,按固定频率放入令牌桶固定个数*/public static void leakWater(){ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(1);scheduledExecutorService.scheduleAtFixedRate(()->{//现在桶中的水int water = waterLeaf.get()-leakRate;//设置当前的水量waterLeaf.set(Math.max(0,water));},1,1, TimeUnit.SECONDS);}public static synchronized boolean limited(int waterCount){if(waterLeaf.get()+waterCount>capacity){//水满了拒绝return true;}waterLeaf.addAndGet(waterCount);System.out.println("此时漏桶水量有: "+waterLeaf);return false;}public static void main(String[] args) {//开始漏水leakWater();//开启请求ScheduledExecutorService executorService=Executors.newScheduledThreadPool(5);AtomicInteger atomicInteger=new AtomicInteger(0);for(int i=0;i<10000;i++){executorService.submit(()->{try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}String taskName=Thread.currentThread().getName();boolean limited = limited(1);if(limited){System.out.println(taskName+"请求被拦截,累计拦截次数"+atomicInteger.incrementAndGet());}else{System.out.println(taskName+"请求访问成功!!!");}});}}}

计数器算法

        计数器算法在一段时间间隔内(时间窗/时间区间),处理请求的最大数量固定,超过部分不做处理。计数器算法是限流算法中最简单的,也是最容易实现的一种算法。

举个例子:

比如:我们规定对于A接口,我们一分钟访问次数不能超过100个。

可以这么做:

1. 在一开始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就+1,如果counter的值大于100,并且该请求与第一个请求的间隔在一分钟之内,那么说明请求数过多,拒绝访问;

2. 如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么重置counter,就是这么简单粗暴

问题:临界问题

计数器限流的严重问题:这个算法虽然简单,但是有一个十分致命的问题,就是临界问题

两分钟之间的临界突发200请求,很危险!!!

这篇关于限流算法(令牌桶漏桶计数器)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/985688

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

redis+lua实现分布式限流的示例

《redis+lua实现分布式限流的示例》本文主要介绍了redis+lua实现分布式限流的示例,可以实现复杂的限流逻辑,如滑动窗口限流,并且避免了多步操作导致的并发问题,具有一定的参考价值,感兴趣的可... 目录为什么使用Redis+Lua实现分布式限流使用ZSET也可以实现限流,为什么选择lua的方式实现

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Redis 多规则限流和防重复提交方案实现小结

《Redis多规则限流和防重复提交方案实现小结》本文主要介绍了Redis多规则限流和防重复提交方案实现小结,包括使用String结构和Zset结构来记录用户IP的访问次数,具有一定的参考价值,感兴趣... 目录一:使用 String 结构记录固定时间段内某用户 IP 访问某接口的次数二:使用 Zset 进行

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

基于Redis有序集合实现滑动窗口限流的步骤

《基于Redis有序集合实现滑动窗口限流的步骤》滑动窗口算法是一种基于时间窗口的限流算法,通过动态地滑动窗口,可以动态调整限流的速率,Redis有序集合可以用来实现滑动窗口限流,本文介绍基于Redis... 滑动窗口算法是一种基于时间窗口的限流算法,它将时间划分为若干个固定大小的窗口,每个窗口内记录了该时间

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系