限流系列:sentinel

目录

滑动窗口算法

Sentinel

数据模型

示例

大致流程

​​​​​​​entry

​​​​​​​entryWithPriority

​​​​​​​FlowSlot.entry

​​​​​​​checkFlow

​​​​​​​canPass

​​​​​​​avgUsedTokens

​​​​​​​passQps

​​​​​​​pass

​​​​​​​currentWindow

calculateTimeIdx

​​​​​​​calculateWindowStart

​​​​​​​values


滑动窗口算法

       滑动窗口算法是将时间周期分为n个小周期,分别记录每个小周期内的访问次数,并且根据时间滑动删除过期的小周期。

       如下图,假设时间周期为1min,将1min再分割成2个小周期,统计每个小周期的访问数量,则可以看到,第一个时间周期内访问数量为75,第二个时间周期内访问数量为100,超过100的数量被限流掉了。


       由此可见,当滑动窗口格子划分得越多,那么滑动窗口的滚动就越平滑,限流的统计就越精确。可以很好的解决固定窗口的流动问题。

Sentinel

       滑动窗口算法也是Sentinel的默认算法。

​​​​​​​数据模型

英文名称

中文名称

备注

array

窗口数组

windowLengthInMs

单个窗口时间长度

sampleCount

总窗口数量

intervalInMs

时间窗口总长度

sampleCount * windowLengthInMs

示例

public static void main(String[] args) throws Exception {
    //加载流控规则
    initFlowRules();
    for (int i = 0; i < 5; i++) {
        Thread.sleep(200);
        Entry entry = null;
        try {
            entry = SphU.entry("sayHello");
            //被保护的逻辑
            log.info("访问sayHello资源");
        } catch (Exception ex) {
            log.info("被流量控制了,可以进行降级处理");
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }
}
private static void initFlowRules() {
    List<FlowRule> rules = new ArrayList<>();
    // 创建一个流控规则
    FlowRule rule = new FlowRule();
    // 对sayHello这个资源限流
    rule.setResource("sayHello");
    // 基于qps限流
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    // qps最大为2,超过2就要被限流
    rule.setCount(2);
    rules.add(rule);
    // 设置规则
    FlowRuleManager.loadRules(rules);
}

大致流程

    点击示例里的entry()方法,如下所示:

​​​​​​​entry

public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {
    return entryWithPriority(resourceWrapper, count, false, args);
}

        点击entryWithPriority()方法,如下所示:

​​​​​​​entryWithPriority

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args) throws BlockException {
    .. ...
    Entry e = new CtEntry(resourceWrapper, chain, context);
    try {
        chain.entry(context, resourceWrapper, null, count, prioritized, args);
    } catch (BlockException e1) {
        e.exit(count, args);
        throw e1;
    } catch (Throwable e1) {
        // This should not happen, unless there are errors existing in Sentinel internal.
        RecordLog.info("Sentinel unexpected exception", e1);
    }
    return e;
}

       点击chain.entry()方法,因为我们这次探究的是限流算法,所以选择FlowSlot类,如下所示:

​​​​​​​FlowSlot.entry

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
    checkFlow(resourceWrapper, context, node, count, prioritized);
    fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

        点击checkFlow()方法,如下所示:

​​​​​​​checkFlow

public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {

        if (ruleProvider == null || resource == null) {

            return;

        }

        Collection<FlowRule> rules = ruleProvider.apply(resource.getName());

        if (rules != null) {

            for (FlowRule rule : rules) {

                if (!canPassCheck(rule, context, node, count, prioritized)) {

                    throw new FlowException(rule.getLimitApp(), rule);

                }

            }

        }

}

​​​​​​​canPass

public boolean canPass(Node node, int acquireCount, boolean prioritized) {
    int curCount = avgUsedTokens(node);
    if (curCount + acquireCount > count) {
        ... ...
        return false;
    }
    return true;
}

       在这里,获取已经使用的token数量,加上待申请的数量,如果超过流控规则里设置的最大值,则返回false。

       点击avgUsedTokens()方法,如下所示:

​​​​​​​avgUsedTokens

private int avgUsedTokens(Node node) {

        if (node == null) {

            return DEFAULT_AVG_USED_TOKENS;

        }

        return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());

}

       在这里,通过调用node.passQps()获取已经使用的token数量。

       点击passQps(),如下所示:

​​​​​​​passQps

public double passQps() {
    return rollingCounterInSecond.pass() / rollingCounterInSecond.getWindowIntervalInSec();
}

       在这里,rollingCounterInSecond对象保存了时间窗口对象的数组。pass()方法可以获取每个有效的时间窗口对象里已经使用的令牌数量,getWindowIntervalInSec()方法是时间窗口总长度,以秒为单位。两者相除就可以已使用的QPS。

       点击pass()方法,如下所示:

​​​​​​​pass

public long pass() {

        data.currentWindow();

        long pass = 0;

        List<MetricBucket> list = data.values();

        for (MetricBucket window : list) {

            pass += window.pass();

        }

        return pass;

    }

       在这里,会调用currentWindow()刷新当前窗口信息,然后累加每个窗口的计数值作为当前计数周期的计数值。

       点击currentWindow()方法,如下所示:

​​​​​​​currentWindow

public WindowWrap<T> currentWindow(long timeMillis) {

    int idx = calculateTimeIdx(timeMillis);

    long windowStart = calculateWindowStart(timeMillis);

    while (true) {

        WindowWrap<T> old = array.get(idx);

        if (old == null) {

            // 创建新窗口

            WindowWrap<T> window = new WindowWrap<>(windowLengthInMs, windowStart, newEmptyBucket());

            if (array.compareAndSet(idx, null, window)) {

                return window;

            }

        } else if (windowStart == old.windowStart()) {

            // 命中当前有效窗口

            return old;

        } else if (windowStart > old.windowStart()) {

            // 窗口已过期,重置并复用

            if (updateLock.tryLock()) {

                try {

                    return resetWindowTo(old, windowStart);

                } finally {

                    updateLock.unlock();

                }

            }

        }

    }

}

在这里:

  1. 获取当前时间窗口的索引
  2. 获取当前窗口的起始时间
  3. 根据当前时间窗口的索引,获取时间窗口对象,如果时间窗口对象为空,则创建一个新时间窗口对象;如果已经存在时间窗口对象,则返回该对象;如果时间窗口对象已过期,则重置并复用。

calculateTimeIdx

public int calculateTimeIdx(long timeMillis) {

    long timeId = timeMillis / windowLengthInMs;

    return (int)(timeId % array.length());

}

       在这里,根据当前时间戳计算对应窗口索引。

​​​​​​​calculateWindowStart

protected long calculateWindowStart(long timeMillis) {

    return timeMillis - timeMillis % windowLengthInMs;

}

       在这里,计算当前窗口的起始时间(对齐到窗口边界)。

       点击步骤pass的values()方法,如下所示:

​​​​​​​values

public List<T> values() {
    return values(TimeUtil.currentTimeMillis());
}
public List<T> values(long timeMillis) {
    if (timeMillis < 0) {
        return new ArrayList<T>();
    }
    int size = array.length();
    List<T> result = new ArrayList<T>(size);
    for (int i = 0; i < size; i++) {
        WindowWrap<T> windowWrap = array.get(i);
        if (windowWrap == null || isWindowDeprecated(timeMillis, windowWrap)) {
            continue;
        }
        result.add(windowWrap.value());
    }
    return result;
}

        在这里,循环时间窗口数组,忽略已经失效的时间窗口对象,将有效的时间窗口对象保存在一个列表对象里,并作为方法返回值进行返回。

​​​​​​​isWindowDeprecated

public boolean isWindowDeprecated(long time, WindowWrap<T> windowWrap) {
    return time - windowWrap.windowStart() > intervalInMs;
}

        在这里,将当前时间减去指定时间窗口对象的起始时间,如果结果大于计数周期时长,则表明指定的时间窗口对象已经失效。

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

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

相关文章

Java 访问者模式深度重构:从静态类型到动态行为的响应式设计实践

一、访问者模式的本质与核心价值 在软件开发的漫长演进中&#xff0c;设计模式始终是架构师手中的利刃。当我们面对复杂对象结构上的多种操作需求时&#xff0c;访问者模式&#xff08;Visitor Pattern&#xff09;犹如一把精密的手术刀&#xff0c;能够优雅地分离数据结构与作…

UE 5 C++设置物体位置和旋转,初始化虚幻引擎样条线、加载引用虚幻编辑器中的蓝图、设置虚幻编辑器中Actor大小

一、设置物体位置和旋转 UE.cpp文件中代码&#xff1a; Mesh->SetWorldLocationAndRotation(FVector(50.0f, 50.0f, 50.0f),FRotator(0,-90,0)); vs代码编辑器中旋转信息顺序&#xff08;yzx&#xff09;&#xff1a; Pitch、 Yaw、 Roll UE编辑器中旋转信息顺序&#xf…

【文本分类】KG-HTC 知识图谱提升分类准确率

最近看到一篇论文“KG-HTC: Integrating Knowledge Graphs into LLMs for Effective Zero-shot Hierarchical Text Classification”&#xff0c;介绍了文本分类的技巧&#xff0c;这篇文航主要利用了知识图谱大模型的思路&#xff0c;实验效果不错&#xff0c;里面的一些论述也…

三大微调技术对比:Prompt/Prefix/P-Tuning

Prompt Tuning、Prefix Tuning和P - Tuning的区别 概念方面: Prompt Tuning:在输入序列前添加可训练的额外Token以适配下游任务,预训练语言模型参数不变。比如在文本分类中,在句子前加特定Token如“(OPINION)”,让模型理解是对观点进行分类的任务。Prefix Tuning:在每层T…

14.「实用」扣子(coze)教程 | Excel文档自动批量AI文档生成实战,中级开篇

随着AI编程工具及其能力的不断发展&#xff0c;编程将变得越来越简单。 在这个大趋势下&#xff0c;大师兄判断未来的编程将真正成为像office工具一样的办公必备技能。每个人通过 &#xff08;专业知识/资源编程&#xff09;将自己变成一个复合型的人才&#xff0c;大大提高生…

量子-经典协同计算新路径:NISQ 时代混合算法对后量子密码学的适应性探索

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨浪味仙 行业动向&#xff1a;3700字丨10分钟阅读 5 月 20 日&#xff0c;由北京量子院、清华大学、数学工程与先进计算国家重点实验室、南洋理工大学、量子信息前沿科学中心…

CentOS中安装Docker Compose

在CentOS中安装Docker Compose的步骤如下&#xff1a; 步骤 1&#xff1a;确保Docker已安装 Docker Compose依赖Docker环境&#xff0c;请先安装Docker&#xff1a; # 添加Docker官方仓库 sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://downlo…

电商小程序店铺详情页:头部无限分类与筛选功能实现

电商小程序店铺详情页:头部无限分类与筛选功能实现 一、场景需求与技术选型二、头部无限分类导航三、筛选功能实现:Picker多列选择组件一、场景需求与技术选型 在电商小程序生态中,店铺详情页作为用户浏览商品的核心流量入口,其交互效率与功能完整性直接影响商品转化率。传…

Graph Neural Network(GNN)

我们首先要了解什么是图,图是由节点和边组成的,边的不一样也导致节点的不同(参考化学有机分子中的碳原子) gnn可以处理classification的问题,也就是分类的问题 也可以处理generation的问题 借一部日剧来说明,这个日剧是讲主角寻找杀害他父亲的凶手的,剧中的人物有姓名和特征 …

FallbackHome的启动流程(android11)

首次开机开机动画播完进入Launcher桌面时黑屏进入Launcher,有黑屏不太美观&#xff0c;在重启以后会在进入桌面后会显示android正在启动等一会进入Launcher,这就是系统FallBackHome机制 接下来我们跟着代码看下首次启动系统如何进入FallbackHome的 在SystemServer的startOthe…

【EdgeYOLO】《EdgeYOLO: An Edge-Real-Time Object Detector》

Liu S, Zha J, Sun J, et al. EdgeYOLO: An edge-real-time object detector[C]//2023 42nd Chinese Control Conference (CCC). IEEE, 2023: 7507-7512. CCC-2023 源码&#xff1a;https://github.com/LSH9832/edgeyolo 论文&#xff1a;https://arxiv.org/pdf/2302.07483 …

宫格导航--纯血鸿蒙组件库AUI

摘要&#xff1a; 宫格导航(A_GirdNav)&#xff1a;可设置导航数据&#xff0c;建议导航项超过16个&#xff0c;可设置“更多”图标指向的页面路由。最多显示两行&#xff0c;手机每行最多显示4个图标&#xff0c;折叠屏每行最多6个图标&#xff0c;平板每行最多8个图标。多余图…

调试的按钮

在Debug的时候&#xff0c;会有一些按钮&#xff0c;我们需要知道它们各自的作用。 注&#xff1a;调试器本身并没有一个直接的、可以撤销已执行代码效果的“返回上一步&#xff08;Undo Last Step&#xff09;”或“逆向执行&#xff08;Reverse Debugging&#xff09;”按钮…

人工智能如何协助老师做课题

第一步&#xff1a;在腾讯元宝对话框中输入如何协助老师做课题&#xff0c;通过提问&#xff0c;我们了解了老师做课题的步骤和建议。 第二步&#xff1a;开题报告提问&#xff0c;腾讯元宝对话框中&#xff0c;输入“大单元视域下小学数学教学实践研究课题开题报告。”......…

OpenGL Chan视频学习-5 Vertex Attributes and Layouts in OpenGL

bilibili视频链接&#xff1a; 【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p5&vd_source44b77bde056381262ee55e448b9b1973 一、知识点整理 1.1.OpenGL管线工作流程 为显卡提供绘制的所有数据&#xff0c;并将数据存储在GPU内存使用着色器&…

Linux_编辑器Vim基本使用

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;LInux_st 小伞的主页&#xff1a;xiaosan_blog 制作不易&#xff01;点个赞吧&#xff01;&#xff01;谢谢喵&#xff01;&a…

MyBatis 高级映射功能详解:处理复杂数据库关系

MyBatis 的高级映射功能是其强大特性之一&#xff0c;它允许开发者轻松处理数据库中的复杂关系&#xff0c;如一对一、一对多和多对多关系。本文将深入探讨这些高级映射功能&#xff0c;包括映射配置方法、嵌套查询和关联查询的使用&#xff0c;并通过示例代码进行演示。 1.数据…

Halo:一个强大易用的国产开源建站工具

Halo 是一款国产开源的建站工具&#xff0c;适合快速搭建博客、论坛、知识库、公司官网等多种类型的网站&#xff0c;目前在 GitHub 上已经获得了 35.6k Star。 功能特性 Halo 核心功能与优势包括&#xff1a; 插件架构&#xff1a;Halo 采用可插拔架构&#xff0c;功能模块之…

Java-ArrayList集合的遍历方式详解

Java-ArrayList集合的遍历方式详解 二、ArrayList概述三、ArrayList的遍历方式1. 普通for循环遍历2. 增强for循环遍历3. 迭代器遍历4. ListIterator遍历5. Java 8 Stream API遍历 四、性能对比与分析性能测试结果分析 五、遍历方式的选择建议六、常见遍历陷阱与注意事项1. 并发…

华为网路设备学习-23(路由器OSPF-LSA及特殊详解 二)

OSPF动态路由协议要求&#xff1a; 1.必须有一个骨干区域&#xff08;Area 0&#xff09;。有且仅有一个&#xff0c;而且连续不可分割。 2.所有非骨干区域&#xff08;Area 1-n&#xff09;必须和骨干区域&#xff08;Area 0&#xff09;直接相连&#xff0c;且所有区域之间…