【深入理解SpringCloud微服务】Sentinel源码解析——FlowSlot流控规则

news2025/1/1 2:41:56

Sentinel源码解析——FlowSlot流控规则

  • StatisticNode与StatisticSlot
    • StatisticNode内部结构
    • StatisticSlot
  • FlowSlot流控规则

在前面的文章,我们对Sentinel的原理进行了分析,Sentinel底层使用了责任链模式,这个责任链就是ProcessorSlotChain对象,链中的每个节点都是一个ProcessorSlot,每个ProcessorSlot对应一个规则的处理。

在这里插入图片描述

然后我们又对Sentinel的整体流程进行了源码分析,我们分析了Sentinel的ProcessorSlotChain对象默认的构成:

在这里插入图片描述

但是我们没有对每个slot进行深入的分析,本篇文章就对它们进行深入的了解。

StatisticNode与StatisticSlot

FlowSlot是根据StatisticSlot中的统计数据进行流控规则校验的,而StatisticSlot的统计数据又是维护在StatisticNode对象中,呈现以下关系:

在这里插入图片描述

StatisticNode内部结构

public class StatisticNode implements Node {

    // 统计1秒内数据的滑动时间窗,1秒内有两个时间窗格,每个时间窗格的时间跨度是500ms
    private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,
        IntervalProperty.INTERVAL);

    // 统计1分钟内数据的滑动时间窗,1分钟内有60个时间窗格,每个时间窗格的时间跨度时1s
    private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);

    /**
     * 当前并发线程数计数器
     */
    private LongAdder curThreadNum = new LongAdder();

	...

}

StatisticNode有三个成员变量,其中rollingCounterInSecond和rollingCounterInMinute都是滑动时间窗计数器,用于分别统计1秒内和1分钟内的数据。

而curThreadNum则是一个LongAdder类型的并发线程数计数器。

在这里插入图片描述

rollingCounterInSecond和rollingCounterInMinute都是Metric类型,Metric是一个接口,实现类是ArrayMetric。

public class ArrayMetric implements Metric {

    private final LeapArray<MetricBucket> data;

	...

}

ArrayMetric里面是一个LeapArray<MetricBucket>。

在这里插入图片描述

public abstract class LeapArray<T> {

    protected int windowLengthInMs;
    protected int sampleCount;
    protected int intervalInMs;
    private double intervalInSecond;

    protected final AtomicReferenceArray<WindowWrap<T>> array;

    public LeapArray(int sampleCount, int intervalInMs) {
        ...
		
		// 单个时间窗口长度 500ms
        this.windowLengthInMs = intervalInMs / sampleCount;
        // 以毫秒为单位的计数器统计的时间跨度 1000ms
        this.intervalInMs = intervalInMs;
        // 以秒为单位的计数器统计的时间跨度 1s
        this.intervalInSecond = intervalInMs / 1000.0;
        // 时间窗口个数 2个
        this.sampleCount = sampleCount;
		
		// 时间窗口数组
        this.array = new AtomicReferenceArray<>(sampleCount);
    }

}

在这里插入图片描述

LeapArray中的成员属性都是根据构造器参数sampleCount(时间窗口个数)和intervalInMs(以毫秒为单位的计数器统计的时间跨度)计算得出的。

其中sampleCount和intervalInMs直接作为LeapArray的成员属性保存。

windowLengthInMs单个时间窗口长度,自然是intervalInMs除以sampleCount获得。rollingCounterInSecond的intervalInMs是1000,sampleCount是2,因此windowLengthInMs为500,表示500ms一个时间窗口。

intervalInSecond是以秒为单位的计数器统计的时间跨度,因此是intervalInMs除以1000,这里的intervalInSecond就是1,代表当前计数器统计的时间范围是1秒。

array是一个AtomicReferenceArray,他就是时间窗口数组,每个窗口是一个WindowWrap对象,泛型是MetricBucket,sampleCount就是AtomicReferenceArray的数组长度,因此是两个时间窗口。

在这里插入图片描述

WindowWrap里面的这个MetricBucket就是真正记录统计数值的。

public class MetricBucket {

	// 记录统计数组的数组,每个下标对应一个指标的统计值
    private final LongAdder[] counters;

    public MetricBucket() {
        MetricEvent[] events = MetricEvent.values();
        this.counters = new LongAdder[events.length];
        for (MetricEvent event : events) {
            counters[event.ordinal()] = new LongAdder();
        }
    }
    
	...
}

MetricBucket里面还有结构,并不是一个单一的value,而是一个LongAdder[],数组中的每个LongAdder对应一个指标的统计,具体有哪些指标,可以查看MetricEvent中的枚举。

public enum MetricEvent {
	// 规则校验通过数
    PASS,
    // 规则校验失败数
    BLOCK,
    // 异常数
    EXCEPTION,
    // 成功数
    SUCCESS,
    // 所有成功调用的响应时间
    RT,
    OCCUPIED_PASS
}

在这里插入图片描述

StatisticSlot

@Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable {
        try {
            fireEntry(context, resourceWrapper, node, count, prioritized, args);
            
            // 增加当前线程数
            node.increaseThreadNum();
            // 增加规则校验通过数
            node.addPassRequest(count);
			...
        } catch (PriorityWaitException ex) {
            ...
        } catch (BlockException e) {
            ...
            // 增加流控规则校验失败计数
            node.increaseBlockQps(count);
            ...
        } catch (Throwable e) {
        	// 设置异常到context
			context.getCurEntry().setError(e);

            throw e;
		}
    }

StatisticSlot的entry方法与其他的slot处理流程不大一样,它是先调用fireEntry方法让slot链继续往后执行。然后后面才进行相关指标的统计。
在这里插入图片描述

@Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        Node node = context.getCurNode();

        if (context.getCurEntry().getBlockError() == null) {
            // 计算响应时间rt
            long completeStatTime = TimeUtil.currentTimeMillis();
            context.getCurEntry().setCompleteTimestamp(completeStatTime);
            long rt = completeStatTime - context.getCurEntry().getCreateTimestamp();

			// 从context中取出error,(如果抛异常,上面的entry方法会设置到context中)
            Throwable error = context.getCurEntry().getError();

            
            recordCompleteFor(node, count, rt, error);
            // ...
        }

        // ...

        fireExit(context, resourceWrapper, count, args);
    }

    private void recordCompleteFor(Node node, int batchCount, long rt, Throwable error) {
        ...
        // 增加响应时间和成功数
        node.addRtAndSuccess(rt, batchCount);
        // 减去当前线程数
        node.decreaseThreadNum();

        if (error != null && !(error instanceof BlockException)) {
        	// 如果有异常,增加异常数
            node.increaseExceptionQps(batchCount);
        }
    }

StatisticSlot的exit方法最后做的就是增加响应时间和成功数以及减去当前线程数;如果context中有异常(就是entry方法塞进去的)还会增加异常数。

在这里插入图片描述

我们发现StatisticSlot做的这些指标统计,全是调用node对象的方法,这个node对象就是StatisticNode。

node.increaseThreadNum()增加并发线程数:
StatisticNode#increaseThreadNum()

    @Override
    public void increaseThreadNum() {
        curThreadNum.increment();
    }

并发线程数是直接加到StatisticNode中的curThreadNum变量中。

而其他的指标都是加到滑动时间窗计数器里面,我们挑一个增加规则校验通过数的node.addPassRequest(count)来看。
StatisticNode#addPassRequest():

    @Override
    public void addPassRequest(int count) {
        rollingCounterInSecond.addPass(count);
        rollingCounterInMinute.addPass(count);
    }

两个滑动时间窗计数器都增加。

在这里插入图片描述

ArrayMetric#addPass(int)

    @Override
    public void addPass(int count) {
    	// 根据当前时间戳定位对应的时间窗口
        WindowWrap<MetricBucket> wrap = data.currentWindow();
        // 时间窗口中的paas计数加1
        wrap.value().addPass(count);
    }

先是根据当前时间戳定位对应的时间窗口,然后把时间窗口中的pass计数加1。

在这里插入图片描述

看下是如何根据当前时间戳定位对应的时间窗口的:
LeapArray#currentWindow()

    public WindowWrap<T> currentWindow() {
        return currentWindow(TimeUtil.currentTimeMillis());
    }

	public WindowWrap<T> currentWindow(long timeMillis) {
		// 根据当前时间戳计算时间窗数组下标
        int idx = calculateTimeIdx(timeMillis);
        // 计算窗口开始时间windowStart
        long windowStart = calculateWindowStart(timeMillis);
        while (true) {
        	// 根据下标取得时间窗
            WindowWrap<T> old = array.get(idx);
            if (old == null) {
                // 定位到的时间窗口为空,创建,窗口开始时间就是windowStart
                WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
                ...
            } else if (windowStart == old.windowStart()) {
            	// 时间窗口的开始时间等于windowStart,表示这个时间窗没过期,返回该时间窗
                return old;
            } else if (windowStart > old.windowStart()) {
            	// 时间窗口的开始时间小于windowStart,表示这个时间窗已过期,重置里面的计数,然后再返回这个时间窗口
                return resetWindowTo(old, windowStart);
            } ...
        }
    }

首先根据当前时间戳计算时间窗数组下标idx,通过下标就可以取得对应的时间窗old = array.get(idx)。

除此以外,还会根据当前时间戳计算一个窗口开始时间windowStart,然后每个时间窗创建的时候都会记录一个开始时间old.windowStart(),两个开始时间一比较,就可得知当前时间窗是否已过期。如果old.windowStart()小于windowStart,那么表示时间窗口old已经过期了。

在这里插入图片描述

根据当前时间戳计算目标窗口下标:
LeapArray#calculateTimeIdx(long)

    private int calculateTimeIdx(long timeMillis) {
    	// 当前时间戳除以当个窗口的时间跨度(500ms),得到timeId
        long timeId = timeMillis / windowLengthInMs;
        // timeId对时间窗数组长度取模,得到下标idx
        return (int)(timeId % array.length());
    }

根据当前时间戳计算窗口开始时间windowStart:
LeapArray#calculateWindowStart(long)

    protected long calculateWindowStart(/*@Valid*/ long timeMillis) {
    	// 当前时间戳 - 当前时间戳 % 当个时间窗的时间跨度
        return timeMillis - timeMillis % windowLengthInMs;
    }

在这里插入图片描述

得到时间窗后,就是执行“wrap.value().addPass(count);”这行代码,首先时间窗WindowWrap的泛型是MetricBucket,因此wrap.value()取到的是MetricBucket对象,然后调用MetricBucket的addPass(count)方法。

MetricBucket#addPass(int)

    public void addPass(int n) {
        add(MetricEvent.PASS, n);
    }

    public MetricBucket add(MetricEvent event, long n) {
    	// counters是个LongAdder[]
    	// PASS对应枚举值0
    	// 因此这里就是对MetricBucket中的LongAdder数组counters中下标为0的LongAdder增加n
        counters[event.ordinal()].add(n);
        return this;
    }

我们上面已经说过MetricBucket中是一个LongAdder[]记录不同指标的计数。而Event.PASS对应的枚举值是0,因此这里就是对LongAdder数组counters中下标为0的LongAdder增加n,正好对应的就是paas指标的LongAdder。

在这里插入图片描述

FlowSlot流控规则

FlowSlot的流控规则校验逻辑全在entry方法中,而exit直接调用fireExit方法往下走,因此我们只看FlowSlot的entry方法即可。

FlowSlot#entry(…)

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable {
		// 流控规则校验
        checkFlow(resourceWrapper, context, node, count, prioritized);

        fireEntry(...);
    }

FlowSlot#checkFlow(…)

    void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
        throws BlockException {
        // 流控规则校验
        checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
    }

FlowRuleChecker#checkFlow(…)

    public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,
                          Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
        ...
        // 从ruleProvider中根据资源名称取得对应的流控规则集合
        Collection<FlowRule> rules = ruleProvider.apply(resource.getName());
        if (rules != null) {
            for (FlowRule rule : rules) {
            	// 逐个校验每个流控规则,一旦有一个校验不通过,则抛出FlowException
                if (!canPassCheck(rule, context, node, count, prioritized)) {
                    throw new FlowException(rule.getLimitApp(), rule);
                }
            }
        }
    }

沿着FlowSlot的entry方法一路进来,到FlowRuleChecker的checkFlow方法。主体流程就是先根据资源名称取到对应的流控规则集合,然后再遍历这个流控规则集合,逐一校验每个流程规则,如果有哪一个规则校验没有通过,那么就抛出FlowException。

在这里插入图片描述

FlowRuleChecker#canPassCheck(FlowRule, …)

    public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) {
        ...
        // 单个流控规则校验
        return passLocalCheck(rule, context, node, acquireCount, prioritized);
    }

    private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount, boolean prioritized) {
        ...
        // 单个流控规则校验
        return rule.getRater().canPass(selectedNode, acquireCount, prioritized);
    }    

“return rule.getRater().canPass(selectedNode, acquireCount, prioritized);”这一行代码有可能进入不同的实现类,视我们选择的“流控效果”而定。

  • DefaultController#canPass(Node, int, boolean):快速失败(滑动时间窗算法)
  • WarmUpController#canPass(Node, int, boolean):Warm Up(令牌桶算法)
  • RateLimiterController#canPass(Node, int, boolean):排队等待(漏桶算法)

在这里插入图片描述

在这里插入图片描述

DefaultController#canPass(Node, int, boolean):

    @Override
    public boolean canPass(Node node, int acquireCount, boolean prioritized) {
    	// 当前的QPS(或者并发线程数,看我们选的“阈值类型”是什么)
        int curCount = avgUsedTokens(node);
        // 加上要增加的数目,如果超了,返回false表示校验失败,没超则返回true表示校验通过
        if (curCount + acquireCount > count) {
            ...
            return false;
        }
        return true;
    }

DefaultController#canPass(Node, int, boolean)方法是流控效果为“快速失败”对应的流控规则校验类,使用的时滑动时间窗算法。首先计算获取当前的QPS或者并发线程数,这个视乎我们选的“阈值类型”而定,然后加上当前要申请的数目acquireCount,如果超过了阈值,返回false表示校验失败,没超则返回true表示校验通过。

在这里插入图片描述

DefaultController#avgUsedTokens(Node):

    private int avgUsedTokens(Node node) {
        ...
        // node.curThreadNum()取得的时并发线程数
        // node.passQps()取得的是QPS
        return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());
    }

node.curThreadNum()其实就是取的StatisticNode中curThreadNum属性的值,它是一个LongAdder类型,上面已经介绍过。
、StatisticNode#curThreadNum()

    @Override
    public int curThreadNum() {
        return (int)curThreadNum.sum();
    }

node.passQps()则要进行计算。
StatisticNode#passQps()

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

在这里插入图片描述

rollingCounterInSecond.pass()取得的是所有的时间窗口pass计数的汇总:
ArrayMetric#pass()

    @Override
    public long pass() {
    	// 这里是判断如果当前的时间窗口过期,则重置它
        data.currentWindow();
        // 所有的时间窗口的paas计数加总到pass
        long pass = 0;
        List<MetricBucket> list = data.values();

        for (MetricBucket window : list) {
            pass += window.pass();
        }
        return pass;
    }

在这里插入图片描述
window.pass()则是取得单个MetricBucket中的pass计数。
MetricBucket#pass():

    public long pass() {
        return get(MetricEvent.PASS);
    }
    public long get(MetricEvent event) {
        return counters[event.ordinal()].sum();
    }

在这里插入图片描述

这样就取得了计数器中每个时间窗口的pass计数加总后的值,但这个并不是QPS,因为有可能这个计数器的时间跨度是大于1秒的,因此还要除以rollingCounterInSecond.getWindowIntervalInSec()。

rollingCounterInSecond.getWindowIntervalInSec()取得的是整个计数器的时间跨度(以秒为单位):
ArrayMetric#getWindowIntervalInSec()

    @Override
    public double getWindowIntervalInSec() {
        return data.getIntervalInSecond();
    }

data是LeapArray类型,进入LeapArray的getIntervalInSecond方法:
LeapArray#getIntervalInSecond()

    public double getIntervalInSecond() {
        return intervalInSecond;
    }

返回的是LeapArray中的intervalInSecond,也就是计数器统计的时间跨度(以秒为单位),那么“rollingCounterInSecond.pass() / rollingCounterInSecond.getWindowIntervalInSec()”得到的就是平均每秒的通过数(pass),也就是QPS。

在这里插入图片描述

以下是FlowSlot流控规则校验的整体流程,结合StatisticNode内部结构的那张大图看,思路就非常清晰了。

在这里插入图片描述

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

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

相关文章

【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版 (1)11

文章目录 一、算法概念11二、算法原理&#xff08;一&#xff09;感知机&#xff08;二&#xff09;多层感知机1、隐藏层2、激活函数sigma函数tanh函数ReLU函数 3、反向传播算法 三、算法优缺点&#xff08;一&#xff09;优点&#xff08;二&#xff09;缺点 四、MLP分类任务实…

R基于贝叶斯加法回归树BART、MCMC的DLNM分布滞后非线性模型分析母婴PM2.5暴露与出生体重数据及GAM模型对比、关键窗口识别

全文链接&#xff1a;https://tecdat.cn/?p38667 摘要&#xff1a;在母婴暴露于空气污染对儿童健康影响的研究中&#xff0c;常需对孕期暴露情况与健康结果进行回归分析。分布滞后非线性模型&#xff08;DLNM&#xff09;是一种常用于估计暴露 - 时间 - 响应函数的统计方法&am…

e3 1220lv3 cpu-z分数

e3 1220lv3 双核四线程&#xff0c;1.1G频率&#xff0c;最低可在800MHZ运行&#xff0c;TDP 13W。 使用PE启动后测试cpu-z分数。 现在e3 1220lv3的价格落到69元。

Debian安装配置RocketMQ

安装配置 本次安装在/tools/rocket目录下 下载 wget https://dist.apache.org/repos/dist/release/rocketmq/5.3.1/rocketmq-all-5.3.1-bin-release.zip 解压缩 unzip rocketmq-all-5.3.1-bin-release.zip 如果出现以下报错 -bash: unzip: command not found可安装unzip工具后执…

cocos 运行时,实时查看、修改节点树

简介 cocos论坛提供的一款辅助查看、修改cocos运行时的节点树工具&#xff0c;同时也可以实时修改运行的节点树并进行修改。在此感谢大佬提供这么实用的工具。 参考链接&#xff1a;【运行时显示节点树插件】ccc-devtools悄悄更新 - Creator 2.x - Cocos中文社区 仓库链接&a…

UE(虚幻)学习(四) 第一个C++类来控制小球移动来理解蓝图和脚本如何工作

UE5视频看了不少&#xff0c;但基本都是蓝图如何搞&#xff0c;或者改一下属性&#xff0c;理解UE系统现有组件使用的。一直对C脚本和蓝图之间的关系不是很理解&#xff0c;看到一个视频讲的很好&#xff0c;我也做笔记记录一下。 我的环境是UE5.3.2. 创建UE空项目 我们创建…

记一次内存泄漏分析(待写稿)

背景 线上Flink频繁重启&#xff0c;先后排查了很多情况&#xff0c;目前在内存阶段排查&#xff0c;首先说说学到的知识 内存泄漏分析 JVM常用命令 JConsole JVisualvm 快照的这里是最有用的&#xff0c;它和jmap不同&#xff0c;jmap查找的是占用字节最多的类&#xff…

【蓝桥杯——物联网设计与开发】拓展模块5 - 光敏/热释电模块

目录 一、光敏/热释电模块 &#xff08;1&#xff09;资源介绍 &#x1f505;原理图 &#x1f505;AS312 &#x1f319;简介 &#x1f319;特性 &#x1f505;LDR &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#x…

C语言从入门到放弃教程

C语言从入门到放弃 1. 介绍1.1 特点1.2 历史与发展1.3 应用领域 2. 安装2.1 编译器安装2.2 编辑器安装 3. 第一个程序1. 包含头文件2. 主函数定义3. 打印语句4. 返回值 4. 基础语法4.1 注释4.1.1 单行注释4.1.2 多行注释 4.2 关键字4.2.1 C语言标准4.2.2 C89/C90关键字&#xf…

第三百四十六节 JavaFX教程 - JavaFX绑定

JavaFX教程 - JavaFX绑定 JavaFX绑定同步两个值&#xff1a;当因变量更改时&#xff0c;其他变量更改。 要将属性绑定到另一个属性&#xff0c;请调用bind()方法&#xff0c;该方法在一个方向绑定值。例如&#xff0c;当属性A绑定到属性B时&#xff0c;属性B的更改将更新属性A…

慧集通客户案例:致远OA与熵基考勤机集成方案

本原型公司是一家专注大健康产业的综合性高新科技形实体企业&#xff0c;按照单位的战略业务布局&#xff0c;围绕“做强做优、世界一流”的目标&#xff0c;加快内外部资源整合、加强业务协同、优化资源配置&#xff0c;有序推进大健康及相关产业的有机融合&#xff0c;加快构…

SCSA: Exploring the Synergistic Effects Between Spatial and Channel Attention

摘要 https://arxiv.org/pdf/2407.05128 通道注意力和空间注意力分别为各种下游视觉任务在提取特征依赖性和空间结构关系方面带来了显著改进。通道注意力和空间注意力的结合使用被广泛认为有利于进一步提升性能&#xff1b;然而&#xff0c;通道注意力和空间注意力之间的协同作…

UE5在蓝图中使用VarestX插件访问API

在Fab中安装好VarestX免费插件 这个插件可以用来远程请求http和api等&#xff0c;返回json等格式内容 插件网址 https://www.fab.com/zh-cn/listings/d283e40c-4ee5-4e73-8110-cc7253cbeaab 虚幻里开启插件 然后网上随便搜个免费api测试一下&#xff0c;这里我找了个微博热搜…

碰一碰发视频矩阵系统源码搭建,支持OEM

一、引言 随着短视频的火爆发展&#xff0c;碰一碰发视频的矩阵系统逐渐受到关注。这种系统能够实现用户通过碰一碰设备&#xff08;如 NFC 标签&#xff09;快速触发视频的发布&#xff0c;在营销推广、互动体验等领域有着广泛的应用前景。本文将详细介绍碰一碰发视频矩阵系统…

Pandas01

文章目录 内容简介1 常用数据分析三方库2 Jupyter notebook3 Series的创建3.1 通过Numpy的Ndarray 创建一个Series3.2 通过列表创建Series 4 Series的属性和方法4.1 常用属性4.2 常用方法4.3 布尔值列表筛选部分数据4.4 Series 的运算 5 DataFrame的创建通过字典创建通过列表[元…

WebPack3项目升级webpack5的配置调试记录

文章目录 前言一、webpack3环境1.1、知识点记录1.1.1、配置解释1.1.2、webpack与sass版本对应关系1.1.3、CommonJS与ESModule1.1.4、node版本管理nvm1.1.5、sass-loader、sass与node-sass 1.2、其他1.2.1、.d.ts是什么文件1.2.2、react与types/react版本对应关系1.2.3、webpack…

plsql :用户system通过sysdba连接数据库--报错ora-01031

一、winR cmd通过命令窗口登录sys用户 sql sys/[password]//localhost:1521/[service_name] as sysdba二、输入用户名:sys as sysdba 三、输入密码:自己设的 四、执行grant sysdba to system; 再去PL/SQL连接就可以了

Quo Vadis, Anomaly Detection? LLMs and VLMs in the Spotlight 论文阅读

文章信息&#xff1a; 原文链接&#xff1a;https://arxiv.org/abs/2412.18298 Abstract 视频异常检测&#xff08;VAD&#xff09;通过整合大语言模型&#xff08;LLMs&#xff09;和视觉语言模型&#xff08;VLMs&#xff09;取得了显著进展&#xff0c;解决了动态开放世界…

药片缺陷检测数据集,8625张图片,使用YOLO,PASICAL VOC XML,COCO JSON格式标注,可识别药品是否有缺陷,是否完整

药片缺陷检测数据集&#xff0c;8625张图片&#xff0c;使用YOLO&#xff0c;PASICAL VOC XML&#xff0c;COCO JSON格式标注&#xff0c;可识别药品是否有缺陷&#xff0c;是否完整 有缺陷的标注信息&#xff1a; 无缺陷的标注信息 数据集下载&#xff1a; yolov11:https://d…

蓝桥杯速成教程{三}(adc,i2c,uart)

目录 一、adc 原理图​编辑引脚配置 Adc通道使能配置 实例测试 ​编辑效果显示 案例程序 badc 按键相关函数 测量频率占空比 main 按键的过程 显示界面的过程 二、IIC通信-eeprom 原理图AT24C02 引脚配置 不可用状态&#xff0c;用的软件IIC 官方库移植 At24c02手册 ​编辑…