限流算法深入

news2024/11/23 15:12:28

限流定义及目的

当系统流量达到系统或下游承受能力的阈值时对系统进行限流控制以防止系统或下游挂掉,减少影响面。

限流组成:阈值及限流策略。阈值是指系统单位时间接收到的请求qps总数;限流策略是指限流行业触发后对应的系统行为,如失败拒绝还是放入等待队列。

应用场景

如秒杀业务,商品变更消息等场景。

秒杀业务:通常来说是保护全链路系统不挂;特性是瞬时流量大;超过阈值时拒绝策略通常是返回失败。

商品变更消息:业务场景是需要将商品变更消息推送给下游商家,下游商家系统能力有限,因此需要做限制;这个场景更多是保护下游系统不挂(整条系统链路上最小短板)。

分类

4大类:计数,滑动窗口,漏桶,令牌。

计数(固定时间窗口)

单位时间窗口内进行计数,超过阈值则进行限流。

算法限制:只能限制一整秒流量。若出现第1秒中后500ms和第2秒中前500ms超过阈值则此时不生效。

实现代码

import exception.BlockException;
import java.util.concurrent.atomic.AtomicInteger;
public class CalNumProcessor {
    //限流窗口大小
    private static int WINDOW_TIME_MILL = 1000;
    //阈值
    private static final int LIMIT_NUMS = 10;
    //计数器
    private AtomicInteger count = new AtomicInteger(0);
    //开始时间
    private Long startTime;

    public static void main(String[] args) throws InterruptedException {
        CalNumProcessor calNumProcessor=new CalNumProcessor();
        for(int i=0;i<1000;i++){
            System.out.println(i);
            calNumProcessor.tryIncAndLimit();
            Thread.sleep(200);
        }
    }

    /**
     * 进行限流计数,超过限流阈值时会抛BlockException
     */
    public void tryIncAndLimit() {
        //当前时间
        long curMill = System.currentTimeMillis();
        //开始时间初始化
        if (startTime == null) {
            startTime = curMill;
        }
        if ((curMill - startTime) > WINDOW_TIME_MILL) {
            //若时间计数超过一秒则重置计数为0并重新设置计数开始时间
            count.set(0);
            startTime = curMill;
        }
        int after = count.incrementAndGet();
        if (after > LIMIT_NUMS) {
            //超过阈值
            throw new BlockException();
        }
    }
}

滑动窗口

计数有个问题在于流量放大无法限流。如1秒为计数窗口。则第一秒后半秒和第二秒前半秒累计可能超过qps限制,但由于不是一个时间窗口此时反而不能限制住。

解决思路:每次请求计数每过一个时间窗口单位进行滑动计数。

整体算法思路详细

1.构建n个时间窗口单位,每个窗口单位有对应qps变量及窗口开始时间;初始化时间窗口单位的qps为0及开始时间为当前时间;

2.每次获取qps时计算当前时间应该在哪个时间窗口单位;

3.循环处理时间窗口,如果发现当前时间与时间窗口单位超过1秒(时间窗口单位最大时间)则重置窗口开始时间及qps为0。

4.将当前时间对应时间窗口qps加1;

5.返回所有时间窗口单位qps累计值;

代码实现

import java.time.LocalTime;
import java.util.concurrent.atomic.AtomicInteger;

public class TimeWindow {

    /**
     * 时间窗口大小,如1秒
     */
    private int windowTimeSize;

    /**
     * 窗口数
     */
    private int windowNum;

    private Window[] windows;

    private int maxQps;

    public TimeWindow(int maxQps, int windowTimeSize, int windowNum) {
        this.maxQps = maxQps;
        this.windowTimeSize = windowTimeSize;
        this.windowNum = windowNum;
        windows = new Window[windowNum];
        for (int i = 0; i < windowNum; i++) {
            windows[i] = new Window();
            windows[i].setQps(new AtomicInteger(0));
            windows[i].setStartTime(System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int qps = 2;
        int count = 20;
        int sleep = 300;
        int success = 0;
        TimeWindow timeWindow = new TimeWindow(qps, 1000, 10);
        for (int i = 0; i < count; i++) {
            Thread.sleep(sleep);
            if (timeWindow.tryAcquire()) {
                success++;
                if (success % qps == 0) {
                    System.out.println(LocalTime.now() + ": success, ");
                } else {
                    System.out.print(LocalTime.now() + ": success, ");
                }
            } else {
                System.out.println(LocalTime.now() + ": fail");
            }
        }
        System.out.println();
        System.out.println("实际测试成功次数:" + success);
    }

    public boolean tryAcquire() {
        //计算当前时间落到哪个窗口位置
        int index = (int) (System.currentTimeMillis() % windowTimeSize) / (windowTimeSize / windowNum);
        //当前时间若超过时间累计窗口则重置窗口参数
        int r = 0;
        for (int i = 0; i < windowNum; i++) {
            Window curWindow = windows[i];
            if ((System.currentTimeMillis() - curWindow.getStartTime()) > windowTimeSize) {
                //当前时间减去时间窗口大于最大累计时间窗口大小则重置变量
                curWindow.setQps(new AtomicInteger(0));
                curWindow.setStartTime(System.currentTimeMillis());
            }
            //当前时间对应窗口累计qps
            if (index == i) {
                curWindow.getQps().incrementAndGet();
            }
            r += curWindow.getQps().get();
        }
        return r <= maxQps;
    }

    class Window {
        /**
         * qps
         */
        private AtomicInteger qps;

        /**
         * 开始时间
         */
        private long startTime;

        public AtomicInteger getQps() {
            return qps;
        }

        public void setQps(AtomicInteger qps) {
            this.qps = qps;
        }

        public long getStartTime() {
            return startTime;
        }

        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }
    }
}

 漏桶算法

流出衡定,不能应对突发流量,能较好保护下游。

令牌算法

优:能处理突出流量,流入相对衡定,流出允许有波动。秒杀场景适用。

这里核心概念:令牌桶(有令牌数及桶上限2个参数),令牌,获取令牌,存放令牌;

存放令牌策略有:1、有单独线路每秒加入n个令牌(相当于qps为n);2、懒计算:当获取令牌请求到来时进行计算,计算思路:Math.min(当前时间距离上次已存放令牌时间间隔秒数*令牌qps,令牌数上限)。

代码

 RateLimiter rateLimiter = RateLimiter.create(2);
        for (int i = 0; i < 10; i++) {
            String time = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
            System.out.println(time + ":" + rateLimiter.tryAcquire());
            Thread.sleep(250);
        }

实现产品

guava,阿里的sentinal。TODO。

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

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

相关文章

WPF+Prism+WebApi 学习总结

一、基本概念 WPF:WPF&#xff08;Windows Presentation Foundation&#xff09;是&#xff08;微软推出的&#xff09;基于Windows的用户界面框架&#xff0c;提供了统一的编程模型&#xff0c;语言和框架&#xff0c;做到了分离界面设计人员与开发人员的工作&#xff1b;WPF…

初学者必看!我的第一个Invideo人工智能文字生成视频

这是一个使用人工智能生成视频的在线平台。 主要功能包括: - 视频脚本自动生成:可以通过输入主题,由AI自动生成视频故事剧本。 - 人声合成:支持上传脚本,AI会合成自然的人声进行朗读。 - 视频制作:有多种视频模板可选择,支持上传自己的素材,一键生成完整视频。 - 特效和增…

JavaScript—数据类型、对象与构造方法

js是什么&#xff1f; JavaScript&#xff08;简称“JS”&#xff09; 是一种具有函数优先的轻量级&#xff0c;解释型或即时编译型的编程语言。JavaScript 基于原型编程、多范式的动态脚本语言&#xff0c;并且支持面向对象、命令式、声明式、函数式编程范式。 js有哪些特点呢…

exchange实战

未得到exchange服务器权限 确定exchange服务器ip地址 setspn -T example.domain.com -F -Q */* | findstr exchangeMailSniper 爆破用户名和密码 爆破Exchange邮箱用户名密码&#xff0c;为了防止账号被锁定&#xff0c;所以我们使用密码喷洒攻击&#xff0c;即只使用一个密…

Windows上安装Hadoop 3.x

目录 0. 安装Java 1. 安装Hadoop 1.1 下载Hadoop 1.2 下载winutils 2. 配置Hadoop 1. hadoop-env.cmd 2. 创建数据目录 3. core-site.xml 4. hdfs-site.xml 3. 启动测试 3.1 namenode格式化 3.2 启动Hadoop 3.3 查看webui 3.4 测试hdfs 3.5. 测试MapReduce 4. 还…

构建个人博客_Obsidian_github.io_hexo

1 初衷 很早就开始分享文档&#xff0c;以技术类的为主&#xff0c;一开始是 MSN&#xff0c;博客&#xff0c;随着平台的更替&#xff0c;后来又用了 CSDN&#xff0c;知乎&#xff0c;简书…… 再后来是 Obsidian&#xff0c;飞书&#xff0c;Notion&#xff0c;常常有以下困…

《Flink学习笔记》——第八章 状态管理

8.1 Flink中的状态 8.1.1 概述 在Flink中&#xff0c;算子任务可以分为无状态和有状态两种情况。 **无状态的算子&#xff1a;**每个事件不依赖其它数据&#xff0c;自己处理完就输出&#xff0c;也不需要依赖中间结果。例如&#xff1a;打印操作&#xff0c;每个数据只需要…

【PLSQL】PLSQL基础

文章目录 一&#xff1a;记录类型1.语法2.代码实例 二&#xff1a;字符转换三&#xff1a;%TYPE和%ROWTYPE1.%TYPE2.%ROWTYPE 四&#xff1a;循环1.LOOP2.WHILE&#xff08;推荐&#xff09;3.数字式循环 五&#xff1a;游标1.游标定义及读取2.游标属性3.NO_DATA_FOUND和%NOTFO…

释放 ChatGPT 的价值:5 个专家提示

随着近来ChatGPT的热议&#xff0c;人工智能技术被推上风口浪尖&#xff0c;由此以数字化技术为基础的数字营销也再次受到了不小的关注&#xff0c;但是营销的本质从来都没有变过&#xff0c;今天我们聊下ChatGPT无论如何演进&#xff0c;人工智能无论变得多么先进&#xff0c;…

设计模式—外观模式(Facade)

目录 一、什么是外观模式&#xff1f; 二、外观模式具有什么优点吗&#xff1f; 三、外观模式具有什么缺点呢&#xff1f; 四、什么时候使用外观模式&#xff1f; 五、代码展示 ①、股民炒股代码 ②、投资基金代码 ③外观模式 思维导图 一、什么是外观模式&#xff1f;…

《Flink学习笔记》——第四章 Flink运行时架构

4.1 系统架构 Flink运行时架构 Flink 运行时由两种类型的进程组成&#xff1a;一个 JobManager 和一个或者多个 TaskManager。 1、作业管理器&#xff08;JobManager&#xff09; JobManager是一个Flink集群中任务管理和调度的核心&#xff0c;是控制应用执行的主进程。也就…

Java之API详解之Object类的详细解析

4 Object类 4.1 概述 tips&#xff1a;重点讲解内容 查看API文档&#xff0c;我们可以看到API文档中关于Object类的定义如下&#xff1a; Object类所在包是java.lang包。Object 是类层次结构的根&#xff0c;每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类…

JavaScript设计模式(二)——简单工厂模式、抽象工厂模式

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

网络防御和入侵检测

网络防御和入侵检测是维护网络安全的关键任务&#xff0c;可以帮助识别和阻止未经授权的访问和恶意行为。以下是一些基本的步骤和方法&#xff0c;用于进行网络防御和入侵检测。 网络防御&#xff1a; 防火墙设置&#xff1a; 部署防火墙来监控和控制网络流量&#xff0c;阻止…

禅道后台命令执行漏洞 (二)

漏洞简介 禅道是第一款国产的开源项目管理软件。它集产品管理、项目管理、质量管理、文档管理、 组织管理和事务管理于一体&#xff0c;是一款专业的研发项目管理软件&#xff0c;完整地覆盖了项目管理的核心流程。禅道管理思想注重实效&#xff0c;功能完备丰富&#xff0c;操…

多线程基础篇

我们平常说的一个程序&#xff0c;一个程序中有声音&#xff0c;图片&#xff0c;字幕 实际上是一个进程中有多个线程 main线程是主线程。 多核&#xff0c;多个cpu&#xff0c;多个线程&#xff0c;切换的很快 单核的话是一个cpu,某一时间只能是一个线程&#xff0c;但是因为…

NLNet、GCNet、RTNet三种多头注意力网络的对比与分析

目录 一、中心思想 二、网络结构的异同点 三、网络结构的改进 3.1 GCNet的改进 3.2 RTNet的改进 四、总结 一、中心思想 三种网络最终目的都是为了捕获远程依赖关系或是全局上下文信息以增强目标检测或是目标分割的效果。 NLNet&#xff1a;卷积运算只能一次处理一个局…

C#,《小白学程序》第七课:列表(List)应用之一“编制高铁车次信息表”

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary>…

17.CSS发光按钮悬停特效

效果 源码 <!DOCTYPE html> <html> <head><title>CSS Modern Button</title><link rel="stylesheet" type="text/css" href="style.css"> </head> <body><a href="#" style=&quo…

【Go 基础篇】探索Go语言中Map的神奇操作

嗨&#xff0c;Go语言的学习者们&#xff01;在编程世界中&#xff0c;Map是一个强大而又有趣的工具&#xff0c;它可以帮助我们高效地存储和操作键值对数据。Map就像是一本字典&#xff0c;可以让我们根据关键字&#xff08;键&#xff09;快速找到对应的信息&#xff08;值&a…