限流算法深入

限流定义及目的

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

限流组成:阈值及限流策略。阈值是指系统单位时间接收到的请求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。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南

Minio的元数据 数据存储 MinIO对象存储系统没有元数据数据库&#xff0c;所有的操作都是对象级别的粒度的&#xff0c;这种做法的优势是: 个别对象的失效&#xff0c;不会溢出为更大级别的系统失效。便于实现"强一致性"这个特性。此特性对于机器学习与大数据处理非…

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

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

基于Java+SpringBoot+Vue前后端分离美食推荐商城设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

2023年7月京东护发市场数据分析(京东数据产品)

如今&#xff0c;与面部护肤相比&#xff0c;多数消费者认为头皮也需要认真对待&#xff0c;这在年轻消费群体中体现的较为明显。 随着消费者对护发理念的认同感不断加深&#xff0c;人们日常居家洗护的步骤也更加精细、使用产品品类也愈加多样化。除传统的护发素、发膜等护发…

mac使用VsCode远程连接服务器总是自动断开并要求输入密码的解决办法

在mac中使用vscode远程连接服务器&#xff0c;时常会出现自动断开并要求重新输入服务器密码的问题&#xff0c;接下来让我们来解决它&#xff1a; 1、首先&#xff0c;在本地创建公钥&#xff1a; ssh-keygen 这条命令执行之后&#xff0c;出现提示直接回车即可&#xff1b;直…

Eclipse打jar包与JavaDOC文档的生成

补充知识点——Eclipse打jar包与JavaDOC文档的生成 1、Eclipse如何打jar包&#xff0c;如何运行jar包 Java当中编写的Java代码&#xff0c;Java类、方法、接口这些东西就是项目中相关内容&#xff0c;到时候我们需要把代码提供给甲方、或者是我们需要运行我们编写的代码&…

【python知识】用 Tkinter实现“剪刀-石头-布”和“弹球游戏 ”

一、提要 Tkinter是一个Python内置模块&#xff0c;它提供了一个简单易用的界面来创建GUI。 在实现一些动态的画面、如游戏还是需要一些创新性思维的。在本文中&#xff0c;我们将使用 Tkinter 探索 Python GUI 编程。我们将介绍 Tkinter 的基础知识&#xff0c;并演示如何使用…

React笔记(一)初识React

一、React概述 1、什么是react react的官网:React 用于构建用户界面的 JavaScript 库&#xff0c;它也是一个渐进式的用于构建用户界面的javascript框架 2、主要特征 声明式&#xff1a;使用原生JS编写的页面存在着开发效率低下、性能较差的情况&#xff0c;使用react大家就…

PAT 1136 A Delayed Palindrome

个人学习记录&#xff0c;代码难免不尽人意 A B C where A is the original number, B is the reversed A, and C is their sum. A starts being the input number, and this process ends until C becomes a palindromic number – in this case we print in the last line …

图文并茂:Python Tkinter从入门到高级实战全解析

目录 介绍什么是Tkinter&#xff1f;准备工作第一个Tkinter程序界面布局事件处理补充知识点 文本输入框复选框和单选框列表框弹出对话框 综合案例&#xff1a;待办事项列表总结 介绍 欢迎来到本篇文章&#xff0c;我们将带您深入了解如何在Python中使用Tkinter库来创建图形用…

拓世科技集团 | “书剑人生”李步云学术思想研讨会暨李步云先生九十华诞志庆

2023年&#xff0c;中国改革开放迎来了45周年&#xff0c;改革春风浩荡&#xff0c;席卷神州大地&#xff0c;45年间&#xff0c;中国特色社会主义伟大事业大步迈入崭新境界&#xff0c;一路上结出了饶为丰硕的果实。中华民族在这45年间的砥砺前行&#xff0c;不仅使中国的经济…

API 接口应该如何设计?如何保证安全?如何签名?如何防重?

说明&#xff1a;在实际的业务中&#xff0c;难免会跟第三方系统进行数据的交互与传递&#xff0c;那么如何保证数据在传输过程中的安全呢&#xff08;防窃取&#xff09;&#xff1f;除了https的协议之外&#xff0c;能不能加上通用的一套算法以及规范来保证传输的安全性呢&am…

机器学习-神经网络(西瓜书)

神经网络 5.1 神经元模型 在生物神经网络中&#xff0c;神经元之间相互连接&#xff0c;当一个神经元受到的外界刺激足够大时&#xff0c;就会产生兴奋&#xff08;称为"激活"&#xff09;&#xff0c;并将剩余的"刺激"向相邻的神经元传导。 神经元模型…

周鸿祎为360智脑招贤纳士;LLM时代的选择指南;Kaggle大语言模型实战;一文带你逛遍LLM全世界 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 思否「齐聚码力」黑客马拉松&#xff0c;用技术代码让生活变得更美好 主页&#xff1a;https://pages.segmentfault.com/google-hacka…

38. 连续签到领金币数

文章目录 题目需求思路一实现一题目来源 题目需求 用户每天签到可以领1金币&#xff0c;并可以累计签到天数&#xff0c;连续签到的第3、7天分别可以额外领2和6金币。 每连续签到7天重新累积签到天数。 从用户登录明细表中求出每个用户金币总数&#xff0c;并按照金币总数倒…

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

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

爬虫逆向实战(二十三)--某准网数据

一、数据接口分析 主页地址&#xff1a;某准网 1、抓包 通过抓包可以发现数据接口是api_to/search/company_v2.json 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现b参数和kiv参数是加密参数 请求头是否加密&#xff1f; 无响应是否加…

2023-08-27 LeetCode每日一题(合并区间)

2023-08-27每日一题 一、题目编号 56. 合并区间二、题目链接 点击跳转到题目位置 三、题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#…

oracle19c-静默安装(centos7)

目录 一.环境准备1.关闭防火墙2.关闭SELINUX3.配置本地yum源4.安装ORACLE先决条件的软件包5.修改LINUX的内核文件6.添加下列参数到/etc/security/limits.conf7.添加下列条目到/etc/pam.d/login8.环境变量中添加下列语句9.创建文件目录和相应的用户10.配置oracle用户的环境变量1…

uniapp 开发微信小程序使用echart的dataZoom属性缩放功能不生效!bug记录!

在本项目中使用的是这个echart库 在项目中添加了dataZoom配置项但是不生效&#xff0c;突然想到微信小程序代码大小的限制&#xff0c;之前的echarts.js是定制的&#xff0c;有可能没有加dataZoom组件。故重新定制echarts.js。之前用的echarts版本是5.0.0&#xff0c;这次也是…