当前位置: 首页 > news >正文

限流算法深入

限流定义及目的

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

限流组成:阈值及限流策略。阈值是指系统单位时间接收到的请求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());}//当前时间对应窗口累计qpsif (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.lryc.cn/news/144020.html

相关文章:

  • java 基础知识 循环的几个题目
  • Spring Boot使用LocalDateTime、LocalDate作为入参
  • 第七周第七天学习总结 | MySQL入门及练习学习第二天
  • 【考研数学】线形代数第三章——向量 | 3)向量组秩的性质、向量空间、过渡矩阵
  • 【技术】SpringBoot Word 模板替换
  • java jni nv21和nv12互转
  • 后端面试话术集锦第二篇:spring boot面试话术
  • Doris中分区和分桶使用教程
  • 电脑不安装软件,怎么将手机文件传输到电脑?
  • vue3 publish 出现的问题
  • 网络防御和入侵检测
  • 【科研论文配图绘制】task5 SciencePlots绘图包入门
  • R语言常用数学函数
  • 公网远程访问局域网SQL Server数据库
  • Apache Celeborn 让 Spark 和 Flink 更快更稳更弹性
  • 华为数通方向HCIP-DataCom H12-821题库(单选题:141-160)
  • Windows-docker集成SRS服务器的部署和使用
  • element-ui table表格滚动条拉到最右侧 表头与内容不能对齐
  • React中的性能测试工具组件Profiler的基本使用
  • 提升生产效率,降低运维成本:纺织业物联网网关应用
  • 【学习笔记】求解线性方程组的G-S迭代法
  • Kotlin协程flow缓冲buffer
  • 完全免费的GPT,最新整理,2023年8月24日,已人工验证,不用注册,不用登录,更不用魔法,点开就能用
  • LeetCode538. 把二叉搜索树转换为累加树
  • TP6 使用闭合语句查询多个or的模型语句
  • 浅析Linux SCSI子系统:设备管理
  • 爬虫逆向实战(二十五)--某矿采购公告
  • DPLL 算法之分裂策略
  • Jmeter+ServerAgent
  • 打破数据孤岛!时序数据库 TDengine 与创意物联感知平台完成兼容性互认