Sentinel架构篇 - 10分钟带你看滑动窗口算法的应用
限流算法
以固定时间窗口算法和滑动时间窗口算法为例,展开两种限流算法的分析。
固定时间窗口算法
在固定的时间窗口内,设置允许固定数量的请求进入。如果超过设定的阈值就拒绝请求或者排队。
具体的,按照时间划分为若干个时间窗口,每个时间窗口里面的设置的请求数量的阈值都是相同的。一旦某个时间窗口内的请求数量达到阈值,就会拒绝新的请求或者进入排队状态。
缺点是无法计算跨相邻时间窗口的请求数量是否达到阈值。
滑动时间窗口算法
在固定时间窗口的基础上,时间窗口的起点和终点不再固定,而是随着时间推移而不断变化,但是每个时间窗口的长度(终点-起点)始终是固定的,也就是说整体会随着时间的推移而不断滑动。
但是每次时间窗口的移动,都需要重新统计请求数量,会有一些重叠区域重复计算的问题,因此可以对时间窗口进行更细粒度的划分,增加一些子时间窗口,即样本窗口。这样对时间窗口内部的请求数量的计算就会变成对相应的样本窗口内部的请求数量的计算,再求和。如果超过阈值,同样会被限流。
源码分析
以 Sentinel 内部的 LeapArray 的 currentWindow 方法为例,解析如何根据指定时间获取对应的样本窗口。
流程概述
1、将指定时间 / 时间窗口的长度(默认500ms),再 % 样本窗口总数量,得到所属的新的样本窗口对应的下标 n。
2、再通过指定时间 - 指定时间 % 时间窗口的长度,得到对应样本窗口的开始时间。
3、从缓存中获取指定下标 n 的旧的样本窗口,如果该样本窗口不存在,则进行创建并返回。
4、比较新样本窗口的开始时间 t1 与旧样本窗口的开始时间 t2,分为三种情况。
- 如果 t1 = t2,说明新样本窗口与旧样本窗口是相同的,则返回旧样本窗口。
- 如果 t1 > t2,说明旧样本窗口的状态滞后,则重置旧样本窗口的所有指标,再使用 LongAdder 计算某一指标并进行更新,最后返回更新后的样本窗口。
- 如果 t1 < t2,说明时间发生了倒流(一般不会发生)则创建新的样本窗口并返回。
LeapArray
public WindowWrap<T> currentWindow(long timeMillis) {if (timeMillis < 0) {return null;}// 计算指定时间所属的样本窗口的下标int idx = calculateTimeIdx(timeMillis);// 计算指定时间所属的样本窗口的起始时间点long windowStart = calculateWindowStart(timeMillis);/** Get bucket item at given time from the array.** (1) Bucket is absent, then just create a new bucket and CAS update to circular array.* (2) Bucket is up-to-date, then just return the bucket.* (3) Bucket is deprecated, then reset current bucket and clean all deprecated buckets.*/while (true) {WindowWrap<T> old = array.get(idx);if (old == null) {/** B0 B1 B2 NULL B4* ||_______|_______|_______|_______|_______||___* 200 400 600 800 1000 1200 timestamp* ^* time=888* bucket is empty, so create new and update** If the old bucket is absent, then we create a new bucket at {@code windowStart},* then try to update circular array via a CAS operation. Only one thread can* succeed to update, while other threads yield its time slice.*/WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));if (array.compareAndSet(idx, null, window)) {return window;} else {Thread.yield();}} else if (windowStart == old.windowStart()) {/** B0 B1 B2 B3 B4* ||_______|_______|_______|_______|_______||___* 200 400 600 800 1000 1200 timestamp* ^* time=888* startTime of Bucket 3: 800, so it's up-to-date** If current {@code windowStart} is equal to the start timestamp of old bucket,* that means the time is within the bucket, so directly return the bucket.*/return old;} else if (windowStart > old.windowStart()) {/** (old)* B0 B1 B2 NULL B4* |_______||_______|_______|_______|_______|_______||___* ... 1200 1400 1600 1800 2000 2200 timestamp* ^* time=1676* startTime of Bucket 2: 400, deprecated, should be reset** If the start timestamp of old bucket is behind provided time, that means* the bucket is deprecated. We have to reset the bucket to current {@code windowStart}.* Note that the reset and clean-up operations are hard to be atomic,* so we need a update lock to guarantee the correctness of bucket update.** The update lock is conditional (tiny scope) and will take effect only when* bucket is deprecated, so in most cases it won't lead to performance loss.*/if (updateLock.tryLock()) {try {// 重置所有指标并计算PASS指标return resetWindowTo(old, windowStart);} finally {updateLock.unlock();}} else {Thread.yield();}} else if (windowStart < old.windowStart()) {// 注意:一般不会出现该情况,该情况属于时间倒流return new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));}}
}
calculateTimeIdx
private int calculateTimeIdx(/*@Valid*/ long timeMillis) {// windowLengthInMs默认是500long timeId = timeMillis / windowLengthInMs;// array数组的长度默认是2return (int)(timeId % array.length());
}
计算指定时间所属的样本窗口的下标。
calculateWindowStart
protected long calculateWindowStart(/*@Valid*/ long timeMillis) {return timeMillis - timeMillis % windowLengthInMs;
}
计算指定时间所属的样本窗口的开始时间。
resetWindowTo
以 OccupiableBucketLeapArray 为例。
@Override
protected WindowWrap<MetricBucket> resetWindowTo(WindowWrap<MetricBucket> w, long time) {// 将windowStart设置为指定时间,即样本窗口的开始时间w.resetTo(time);// 获取样本窗口的统计数据MetricBucket borrowBucket = borrowArray.getWindowValue(time);if (borrowBucket != null) {// 重置所有指标w.value().reset();// 计算PASS指标w.value().addPass((int)borrowBucket.pass());} else {// 重置所有指标w.value().reset();}return w;
}
MetricBucket#reset
public MetricBucket reset() {for (MetricEvent event : MetricEvent.values()) {// 重置所有指标counters[event.ordinal()].reset();}// 初始化minRt属性initMinRt();return this;
}
MetricEvent 是一个枚举类,包括:PASS, BLOCK, EXCEPTION, SUCCESS, RT, OCCUPIED_PASS。
MetricBucket#initMinRt
private void initMinRt() {// 获取 csp.sentinel.statistic.max.rt 属性值(默认 5000),并初始化minRt属性this.minRt = SentinelConfig.statisticMaxRt();
}
MetricBucket#addPass
public void addPass(int n) {// 计算PASS指标add(MetricEvent.PASS, n);
}public MetricBucket add(MetricEvent event, long n) {// 底层使用LongAdder计算指标counters[event.ordinal()].add(n);return this;
}
相关文章:
Sentinel架构篇 - 10分钟带你看滑动窗口算法的应用
限流算法 以固定时间窗口算法和滑动时间窗口算法为例,展开两种限流算法的分析。 固定时间窗口算法 在固定的时间窗口内,设置允许固定数量的请求进入。如果超过设定的阈值就拒绝请求或者排队。 具体的,按照时间划分为若干个时间窗口&#…...

redis主从复制
<1> redis主从复制介绍: 首先来介绍一下什么是redis主从复制 Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受…...

近期常见组件漏洞更新:
(1)mysql 5.7 在2023年1月17日,发布了到5.7.41版本 mysql 8.0 在2023年1月17日,发布了到8.0.32版本 MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ (2)Tomcat8在202…...

深度学习常用的激活函数总结
各种激活函数总结 目录一、sigmoid二、tanh三、ReLU系列1.原始ReLU2.ReLU改进:Leaky ReLU四、swish五、GeLU一、sigmoid 优点: 1.可以将任意范围的输出映射到 …...
Java编程问题top100---基础语法系列(二)
Java编程问题top100---基础语法系列(二)六、如何测试一个数组是否包含指定的值?简单且优雅的方法:自己动手写逻辑对象数组JDK 8 APIJDK 9 API Set.of()七、重写(Override)equlas和hashCode方法时应考虑的问题理论上讲&…...

网页打印与导出word实现在A4纸上相同效果
在工作中遇到这样一个需求,客户要求: 1、实现在浏览器中打印和导出到word中,要求浏览器打印出来的效果和word中打印的效果基本一致。2、打印的内容要自动分页,第一页的顶部有文件头,最后一页的底部有页尾。 这里记录一…...

备战英语6级——记录复习进度
开始记录—— 学习:如何记录笔记? 1:首先我认为:电脑打字比较适合我! 2:先记笔记,再“填笔记”! 记笔记就是一个框架,记录一个大概的东西。后面需要在笔记中࿰…...

实例10:四足机器人运动学逆解可视化与实践
实例10: 四足机器人运动学逆解单腿可视化 实验目的 了解逆运动学的有无解、有无多解情况。了解运动学逆解的求解。熟悉逆运动学中求解的几何法和代数法。熟悉单腿舵机的简单校准。掌握可视化逆向运动学计算结果的方法。 实验要求 拼装一条mini pupper的腿部。运…...
Elasticsearch7.8.0版本优化——路由选择
目录一、Elasticsearch 如何知道一个文档存放在哪个分片二、不带 routing 查询三、带 routing 查询一、Elasticsearch 如何知道一个文档存放在哪个分片 其实是通过这个公式来计算出来:shard hash(routing) % number_of_primary_shardsrouting 默认值是文档的 id&a…...
Go常量的定义和使用const,const特性“隐式重复前一个表达式”,以及iota枚举常量的使用
Go常量的定义和使用const,以及iota枚举常量的使用Go常量constGo中常量的定义和使用Go特性const,"隐式重复前一个表达式"iota 实现枚举常量Go常量const Go语言中的const整合了C语言中的宏定义常量,const只读变量枚举变量 绝大多数情况下,Go常…...

Git学习(1)pro git阅读
目录 目录: 1. 起步 2. Git 基础 3. Git 分支 4. 服务器上的 Git 5. 分布式 Git 第一章 1.3 Git是什么 1.6运行git前的配置 该开源图书网站 Git - Book (git-scm.com) 目录: 1. 起步 1.1 关于版本控制1.2 Git 简史1.3 Git 是什么?1…...

PHY自协商
1. 自协商定义 自动协商模式是端口根据另一端设备的连接速度和双工模式,自动把它的速度调节到最高的公共水平,即线路两端能具有的最快速度和双工模式。 自协商功能允许一个网络设备能够将自己所支持的工作模式信息传达给网络上的对端,并接受对…...

【大数据离线开发】8.2 Hive的安装和配置
8.3 Hive的安装和配置 安装模式: 嵌入模式 :不需要使用MySQL,需要Hive自带的一个关系型数据库:Derby本地模式、远程模式 ----> 需要MySQL数据库的支持 安装 hive 安装包 1、解压tar -zxvf apache-hive-2.3.0-bin.tar.gz -C…...

Capture Modules:车载网络报文捕获模块
(以下所有图片均来源于Technica官网) Technica Engineering的新一代硬件设备,即Capture Modules,提供了五种变体以涵盖不同带宽的车载以太网(100BASE-T1和1000BASE-T1)以及常见的IVN技术(CAN、C…...

数据结构与算法系列之时间与空间复杂度
这里写目录标题算法的复杂度大O的渐进表示法实例分析空间复杂度每日一题算法的复杂度 衡量一个算法的好坏,一般 是从时间和空间两个维度来衡量的, 即时间复杂度和空间复杂度。 时间复杂度主要衡量一个算法的运行快慢, 空间复杂度主要衡量一个…...

Python代码使用PyQt5制作界面并封装
目录参考链接续:https://blog.csdn.net/yulinxx/article/details/93344163 若要对此程序进行封装,加个界面,然后制作成 EXE, 使用 PyQt5 制作界面,PyInstaller 进行封装成 EXE 可参考: Python制作小软件…...

【Node.js】MySQL数据库的第三方模块(mysql)
mysql安装操作MySQL数据库的第三方模块(mysql)通过第三方模块(mysql2)连接到MySQL数据库mysql插入数据mysql插入数据的便捷方式mysql更新数据mysql更新数据的便捷方式mysql删除数据安装操作MySQL数据库的第三方模块(my…...

Docker中安装并配置单机版redis
1、使用docker安装redis 搜索Reis镜像,这里展示的是官方最新的镜像docker search redis 使用官方dockerhub搜索redis 2、选用常用的redis5.0作为安装的版本docker pull redis:5.0 3、运行redis容器的两种方式 3.1 不映射外部配置文件直接运行redis5.0镜像docker …...

模拟微信聊天-课后程序(JAVA基础案例教程-黑马程序员编著-第八章-课后作业)
【案例9-1】 模拟微信聊天 【案例介绍】 1.案例描述 在如今,微信聊天已经人们生活中必不可少的重要组成部分,人们的交流很多都是通过微信来进行的。本案例要求:将多线程与UDP通信相关知识结合,模拟实现微信聊天小程序。通过监…...

html2canvas将页面dom元素内容渲染成图片保存至本地
html2canvas:https://html2canvas.hertzen.com/configuration/ github:https://github.com/niklasvh/html2canvas 效果 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compa…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...