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

Sentinel架构篇 - 熔断降级

熔断降级

概念

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用其它模块,可能是一个远程服务、数据库、或者第三方 API 等。然而,被依赖的服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,导致请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会堆积,最终可能会耗尽业务自身的线程池,甚至服务本身变得不可用。

现在的微服务架构都是分布式的,由非常多的服务组成。不同的服务之间相互调用,形成复杂的调用链路。链路中某一环不稳定,可能会层层级联,最终导致整个链路不可用。因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定的调用,避免局部不稳定因素导致整体的雪崩。熔断降级通常在客户端(调用端)进行配置。

熔断可以类比成生活中的保险丝,一旦电流过载,保险丝就会断开。

Sentinel 熔断降级基于熔断器模式 (circuit breaker pattern) 实现。熔断器内部维护了一个熔断器的状态机,状态机的转换关系如下图所示:

在这里插入图片描述

熔断器有三种状态:

  • Closed 状态:也是初始状态,该状态下,熔断器会保持闭合,对资源的访问直接通过熔断器的检查。
  • Open 状态:断开状态,熔断器处于开启状态,对资源的访问会被切断。
  • Half-Open 状态:半开状态,该状态下除了探测流量,其余对资源的访问也会被切断。探测流量指熔断器处于半开状态时,会周期性的允许一定数目的探测请求通过,如果探测请求能够正常的返回,代表探测成功,此时熔断器会重置状态到 Closed 状态,结束熔断;如果探测失败,则回滚到 Open 状态。

熔断策略

Sentinel 提供了如下三种熔断策略。

  • 慢调用比例(SLOW_REQUEST_RATIO):选择慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),如果请求的时间大于该阈值则被统计为慢调用。当单位统计时长(statIntervalMs)内请求的数量大于设置的最小请求数量,并且慢调用的比例大于阈值,则接下来的熔断时长(timeWindow)内请求自动被熔断。经过熔断时长后,熔断器进入探测恢复状态(Half-Open 状态),如果接下来的一个请求的响应时间小于设置的慢调用 RT 则结束熔断;如果大于则再次被熔断。慢调用比例的阈值范围为 [0.0, 1.0],代表 0% - 100%。
  • 异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数量大于设置的最小请求数量,并且异常比例大于阈值,则接下来的熔断时长(timeWindow)内请求自动被熔断。经过熔断时长后,熔断器进入探测恢复状态(Half-Open 状态),如果接下来的一个请求成功完成(没有错误)则结束熔断;否则再次被熔断。异常比例的阈值范围为 [0.0, 1.0] ,代表 0% - 100%。
  • 异常数(ERROR_COUNT):当单位统计时长(statIntervalMs)内的异常数量超过阈值之后,则接下来的熔断时长(timeWindow)内请求自动被熔断。经过熔断时长后,熔断器进入探测恢复状态(Half-Open 状态),如果接下来的一个请求成功完成(没有错误)则结束熔断;否则再次被熔断。

熔断降级规则

字段说明默认值
resource资源名,即规则作用的对象
grade熔断策略,支持慢调用比例/异常比例/异常数慢调用比例
count慢调用比例模式下对应慢调用RT(超过该值即为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为秒
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比例超过阈值也不会熔断(1.7.0 版本引入)5
statIntervalMs统计时长,单位为毫秒(1.8.0 版本引入)1000
slowRationThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 版本引入)

同一个资源可以同时有多个熔断降级规则。

实际操作

在 Nacos 的控制台中的配置管理/配置列表中,在 public 的命名空间中创建一个如下配置:

dataId:spring-cloud-demo-consumer-sentinel-degrade

group:DEFAULT

[{"resource": "/hello/say","limitApp": "default","grade": 0,"count": 200,"timeWindow": 10,"statIntervalMs": 1000,"slowRatioThreshold": 0.6   }
]

如果 1 秒内,请求数量至少达到 200,并且(请求的响应时间超过 200 毫秒即为慢调用)慢调用的比例达到 60%,则进行熔断,熔断时长为 10 秒。


对应的客户端的配置文件如下:

spring:application:name: spring-cloud-demo-consumercloud:nacos:discovery:server-addr: 10.211.55.11:8848,10.211.55.12:8848,10.211.55.13:8848enabled: truesentinel:transport:dashboard: 127.0.0.1:9000eager: truedatasource:degrade-nacos-datasource:nacos:server-addr: 10.211.55.11:8848,10.211.55.12:8848,10.211.55.13:8848group-id: DEFAULT_GROUPnamespace: publicdata-id: ${spring.application.name}-sentinel-degradedata-type: jsonrule-type: degradeusername: nacospassword: nacos

DegradeSlot

负责熔断降级规则的判断。

@Spi(order = Constants.ORDER_DEGRADE_SLOT)
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {// 校验熔断降级规则performChecking(context, resourceWrapper);fireEntry(context, resourceWrapper, node, count, prioritized, args);}void performChecking(Context context, ResourceWrapper r) throws BlockException {// 由DegradeRuleManager负责加载所有的断路器List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());// 如果断路器列表为空,则直接返回if (circuitBreakers == null || circuitBreakers.isEmpty()) {return;}// 遍历断路器列表,只要有一个断路器判定请求不通过,则抛出DegradeException异常for (CircuitBreaker cb : circuitBreakers) {if (!cb.tryPass(context)) {throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());}}}@Overridepublic void exit(Context context, ResourceWrapper r, int count, Object... args) {Entry curEntry = context.getCurEntry();// 如果调用过程中存在BlockException异常,则直接返回if (curEntry.getBlockError() != null) {fireExit(context, r, count, args);return;}// 由DegradeRuleManager负责加载所有的断路器List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());// 如果断路器列表为空,则直接返回if (circuitBreakers == null || circuitBreakers.isEmpty()) {fireExit(context, r, count, args);return;}// 如果调用过程中不存在BlockException异常if (curEntry.getBlockError() == null) {// 遍历断路器列表,触发每个断路器的onRequestComplete方法的回调for (CircuitBreaker circuitBreaker : circuitBreakers) {circuitBreaker.onRequestComplete(context);}}fireExit(context, r, count, args);}
}

接下来看下断路器如何判断请求是否通过的。

AbstractCircuitBreaker

AbstractCircuitBreaker(DegradeRule rule, EventObserverRegistry observerRegistry) {AssertUtil.notNull(observerRegistry, "observerRegistry cannot be null");if (!DegradeRuleManager.isValidRule(rule)) {throw new IllegalArgumentException("Invalid DegradeRule: " + rule);}this.observerRegistry = observerRegistry;this.rule = rule;this.recoveryTimeoutMs = rule.getTimeWindow() * 1000;
}

接下来看下 tryPass 方法的处理逻辑。

@Override
public boolean tryPass(Context context) {// 如果断路器的状态是CLOSED,则返回true,表示请求通过if (currentState.get() == State.CLOSED) {return true;}// 如果断路器的状态是OPENif (currentState.get() == State.OPEN) {// 判断是否到了熔断结束时间,如果到了则尝试将断路器的状态从OPEN变为HALF-OPENreturn retryTimeoutArrived() && fromOpenToHalfOpen(context);}// 剩余情况,返回false,表示请求不通过return false;
}

retryTimeoutArrived 方法

判断是否到了熔断结束时间

protected boolean retryTimeoutArrived() {// 判断当前时间 >= 下一次的熔断结束时间return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
}

fromOpenToHalfOpen 方法

尝试将断路器的状态从OPEN变为HALF-OPEN

protected boolean fromOpenToHalfOpen(Context context) {// 尝试将断路器的状态从OPEN更新为HALF_OPENif (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {// 触发所有CircuitBreakerStateChangeObserver的onStateChange方法回调notifyObservers(State.OPEN, State.HALF_OPEN, null);Entry entry = context.getCurEntry();entry.whenTerminate(new BiConsumer<Context, Entry>() {@Overridepublic void accept(Context context, Entry entry) {// 如果调用过程中存在BlockException异常if (entry.getBlockError() != null) {// 将断路器的状态从HALF_OPEN更新为OPENcurrentState.compareAndSet(State.HALF_OPEN, State.OPEN);// 触发所有CircuitBreakerStateChangeObserver的onStateChange方法回调notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);}}});return true;}return false;
}

接下来重点看下 AbstractCircuitBreaker 的子类对于 onRequestComplete 方法的具体实现。

ResponseTimeCircuitBreaker

关注响应时间的断路器实现

public ResponseTimeCircuitBreaker(DegradeRule rule) {// 统计时长由熔断降级规则的statIntervalMs参数指定,默认1000,即1秒this(rule, new SlowRequestLeapArray(1, rule.getStatIntervalMs()));
}ResponseTimeCircuitBreaker(DegradeRule rule, LeapArray<SlowRequestCounter> stat) {super(rule);AssertUtil.isTrue(rule.getGrade() == RuleConstant.DEGRADE_GRADE_RT, "rule metric type should be RT");AssertUtil.notNull(stat, "stat cannot be null");this.maxAllowedRt = Math.round(rule.getCount());this.maxSlowRequestRatio = rule.getSlowRatioThreshold();this.minRequestAmount = rule.getMinRequestAmount();this.slidingCounter = stat;
}

看下 ResponseTimeCircuitBreaker 对于 onRequestComplete 方法的具体实现。

@Override
public void onRequestComplete(Context context) {SlowRequestCounter counter = slidingCounter.currentWindow().value();Entry entry = context.getCurEntry();if (entry == null) {return;}long completeTime = entry.getCompleteTimestamp();if (completeTime <= 0) {completeTime = TimeUtil.currentTimeMillis();}long rt = completeTime - entry.getCreateTimestamp();// 如果响应时间超过了阈值(对应熔断降级规则中的count参数)if (rt > maxAllowedRt) {// 慢请求数指标加一counter.slowCount.add(1);}// 总请求数指标加一counter.totalCount.add(1);handleStateChangeWhenThresholdExceeded(rt);
}

接下来看下 handleStateChangeWhenThresholdExceeded 方法的处理逻辑。

private void handleStateChangeWhenThresholdExceeded(long rt) {// 如果断路器的状态是OPEN,则直接返回if (currentState.get() == State.OPEN) {return;}// 如果断路器的状态是HALF_OPENif (currentState.get() == State.HALF_OPEN) {// 如果请求的响应时间超过了阈值if (rt > maxAllowedRt) {// 将断路器的状态更新为OPEN,然后更新下一次的熔断结束时间fromHalfOpenToOpen(1.0d);} else {// 将断路器的状态更新为CLOSED,然后重置慢请求数、总请求数指标fromHalfOpenToClose();}return;}List<SlowRequestCounter> counters = slidingCounter.values();long slowCount = 0;long totalCount = 0;// 累加慢请求数、总请求数for (SlowRequestCounter counter : counters) {slowCount += counter.slowCount.sum();totalCount += counter.totalCount.sum();}// 如果总请求数 < 熔断降级规则中的minRequestAmount参数,则直接返回if (totalCount < minRequestAmount) {return;}// 计算慢调用比例double currentRatio = slowCount * 1.0d / totalCount;// 如果慢调用比例 > 熔断降级队则中的slowRatioThreshold参数值(默认1)if (currentRatio > maxSlowRequestRatio) {// 将断路器的状态更新为OPEN,然后更新下一次的熔断结束时间transformToOpen(currentRatio);}// 如果当前的慢调用比例达到了100%if (Double.compare(currentRatio, maxSlowRequestRatio) == 0 &&Double.compare(maxSlowRequestRatio, SLOW_REQUEST_RATIO_MAX_VALUE) == 0) {// 将断路器的状态更新为OPEN,然后更新下一次的熔断结束时间transformToOpen(currentRatio);}
}

ExceptionCircuitBreaker

关注异常比例、异常数的断路器实现

public ExceptionCircuitBreaker(DegradeRule rule) {// 统计时长由熔断降级规则的statIntervalMs参数指定,默认1000,即1秒this(rule, new SimpleErrorCounterLeapArray(1, rule.getStatIntervalMs()));
}ExceptionCircuitBreaker(DegradeRule rule, LeapArray<SimpleErrorCounter> stat) {super(rule);this.strategy = rule.getGrade();boolean modeOk = strategy == DEGRADE_GRADE_EXCEPTION_RATIO || strategy == DEGRADE_GRADE_EXCEPTION_COUNT;AssertUtil.isTrue(modeOk, "rule strategy should be error-ratio or error-count");AssertUtil.notNull(stat, "stat cannot be null");this.minRequestAmount = rule.getMinRequestAmount();this.threshold = rule.getCount();this.stat = stat;
}

看下 ExceptionCircuitBreaker 对于 onRequestComplete 方法的具体实现。

@Override
public void onRequestComplete(Context context) {Entry entry = context.getCurEntry();if (entry == null) {return;}Throwable error = entry.getError();SimpleErrorCounter counter = stat.currentWindow().value();if (error != null) {// 对异常请求数指标加一counter.getErrorCount().add(1);}// 对总请求数指标加一counter.getTotalCount().add(1);handleStateChangeWhenThresholdExceeded(error);
}

接下来看下 handleStateChangeWhenThresholdExceeded 方法的处理逻辑。

private void handleStateChangeWhenThresholdExceeded(Throwable error) {// 如果断路器的状态是OPEN,则直接返回if (currentState.get() == State.OPEN) {return;}// 如果断路器的状态是HALF_OPENif (currentState.get() == State.HALF_OPEN) {if (error == null) {// 将断路器的状态更新为CLOSED,然后重置慢请求数、总请求数指标fromHalfOpenToClose();} else {// 将断路器的状态更新为OPEN,然后更新下一次的熔断结束时间fromHalfOpenToOpen(1.0d);}// 直接返回return;}List<SimpleErrorCounter> counters = stat.values();long errCount = 0;long totalCount = 0;// 累加错误请求数、总请求数for (SimpleErrorCounter counter : counters) {errCount += counter.errorCount.sum();totalCount += counter.totalCount.sum();}// 如果总请求数 < 熔断降级规则中的minRequestAmount参数,则直接返回if (totalCount < minRequestAmount) {return;}double curCount = errCount;// 如果策略是统计异常数比例,则将异常数比例转化成错误请求数if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {curCount = errCount * 1.0d / totalCount;}// 如果错误请求数 > 阈值if (curCount > threshold) {// 将断路器的状态更新为OPEN,然后更新下一次的熔断结束时间transformToOpen(curCount);}
}

相关文章:

Sentinel架构篇 - 熔断降级

熔断降级 概念 除了流量控制以外&#xff0c;对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用其它模块&#xff0c;可能是一个远程服务、数据库、或者第三方 API 等。然而&#xff0c;被依赖的服务的稳定性是不能保证的。如果依赖的服…...

shell脚本的一些记录 与jenkins的介绍

shell 脚本的执行 sh ***.sh shell脚本里面的命令 其实就是终端执行一些命令 shell 连接服务器 可以直接ssh连接 但是这样最好是无密码的 不然后面的命令就不好写了 换而言之有密码得 不好写脚本 需要下载一些expect的插件之类的才可以 判断语句 的示例 需要注意的是…...

JVM的了解与学习

一:jvm是什么 jvm是java虚拟机java Virtual Machine的缩写 jdk包含jre和java DevelopmentTools 二:什么是java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。java虚拟机有自己完善的硬体结构,如处理器、堆栈、寄存器等,还有…...

提升数字品牌的5个技巧

“品牌”或“品牌推广”的概念通常用于营销。因为建立您的企业品牌对于产品来说极其重要&#xff0c;品牌代表了您与客户互动的身份和声音。今天&#xff0c;让我们来看看在数字领域提升品牌的一些有用的技巧。如何在数字领域提升您的品牌&#xff1f;在了解这些技巧之前&#…...

java通过反射获取加了某个注解的所有的类

有时候我们会碰到这样的情况&#xff1a;有n个场景&#xff0c;每个场景都有自己的逻辑&#xff0c;即n个处理逻辑&#xff0c;这时候我们就需要通过某个参数的值代表这n个场景&#xff0c;然后去加载每个场景不同的bean对象&#xff0c;即不同的类&#xff0c;这些类中都有一个…...

Warshall算法

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;> 算法 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…...

vector中迭代器失效的问题及解决办法

目录 vector常用接口 vector 迭代器失效问题 vector中深浅拷贝问题 vector的数据安排以及操作方式&#xff0c;与array非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间&#xff0c;一旦配置了就不能改变&#xff1b;要换个大(或小) 一点的房子&#x…...

【蓝桥杯刷题训练营】day05

1 数的分解 拆分成3个数相加得到该数 然后采用了一种巨愚蠢的办法&#xff1a; int main() {int count 0;int a 2;int b 0;int c 1;int d 9;int a1, a2, a3;int c1, c2, c3;int d1, d2, d3;for (a1 0; a1 < 2; a1){for (a2 0; a2 < 2; a2){for (a3 0; a3 <…...

线程中断interrupt导致sleep产生的InterruptedException异常

强制当前正在执行的线程休眠&#xff08;暂停执行&#xff09;&#xff0c;以“减慢线程”。 Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法当线程睡眠时&#xff0c;它睡在某个地方&#xff0c;在苏醒之前不会返回到可运行状态。 当睡眠时间到期…...

ubuntu的快速安装与配置

文章目录前言一、快速安装二 、基础配置1 Sudo免密码2 ubuntu20.04 pip更新源3 安装和配置oneapi(infort/mpi/mkl) apt下载第一次下载的要建立apt源apt下载&#xff08;infort/mpi/mkl)4 安装一些依赖库等5 卸载WSLpython总结前言 win11系统 ubuntu20.04 提示&#xff1a;以下…...

人工智能AI工具汇总(AIGC ChatGPT时代个体崛起)

NameCategoryWebsiteDescription描述《AIGC时代&#xff1a;超级个体的崛起》小报童https://xiaobot.net/p/SuperIndividual 介绍AIGC&#xff0c;ChatGPT&#xff0c;使用技巧与搞钱方式。Masterpiece Studio3Dhttps://masterpiecestudio.comSimplifying 3D Creation with AI…...

【rust-grpc-proxy】在k8s中,自动注入代理到pod中,再不必为grpc调试而烦恼

目录前言原理sidecarwebhook实现安装k8s设置webhook使用尾语前言 rust-grpc-proxy 目前功能基本完善。是时候上环境开始应用了。 之前考虑是gateway模式或者sidecar模式。 思考良久之后&#xff0c;觉得两种模式都有使用场景&#xff0c;那就都支持。本次就带来sidecar模式的食…...

VisualStudio2022制作多项目模板及Vsix插件

一、安装工作负载 在vs2022上安装“visual studio扩展开发 ”工作负载 二、制作多项目模板 导出项目模板这个我就不再多说了&#xff08;项目→导出模板→选择项目模板&#xff0c;选择要导出的项目→填写模板信息→完成&#xff09;。 1.准备模板文件 将解决方案中的多个…...

仿写简单IOC

目录 TestController类: UserService类: 核心代码SpringIOC&#xff1a; Autowired和Component注解 SpringIOCTest 类 ​编辑 总结&#xff1a; TestController类: Component public class TestController {Autowiredprivate UserService userService;public void test…...

liunx下安装node exporter

1 建立文件夹 cd /opt mkdir software 下载最新的包&#xff0c;并解压 https://prometheus.io/download/ 下载 curl -LO https://github.com/prometheus/node_exporter/releases/download/v0.18.1/node_exporter-0.18.1.linux-amd64.tar.gz 3.解压 tar -xvf node_exporter-0.…...

lambda函数

Lambda(函数指针)lambda 是c11非常重要也是最常用的特性之一&#xff0c;他有以下优点&#xff1a;可以就地匿名定义目标函数或函数对象&#xff0c;不需要额外写一个函数lambda表达式是一个匿名的内联函数lambda表达式定义了一个匿名函数&#xff0c;语法如下&#xff1a;[cap…...

【Python入门第二十七天】Python 日期

Python 日期 Python 中的日期不是其自身的数据类型&#xff0c;但是我们可以导入名为 datetime 的模块&#xff0c;把日期视作日期对象进行处理。 实例 导入 datetime 模块并显示当前日期&#xff1a; import datetimex datetime.datetime.now() print(x)运行实例 2023-0…...

C++基础知识【5】数组和指针

目录 一、概述 数组 指针 二、数组 2.1、数组的声明 2.2、数组的初始化 2.3、数组的访问 2.4、多维数组 2.5、数组作为函数参数 三、指针 3.1、指针的声明 3.2、指针的赋值 3.3、指针的访问 3.4、指针运算 3.5、指针数组和数组指针 3.6、二级指针 四、数组和指…...

Vim使用操作命令笔记

Vim使用操作命令笔记在普通模式下&#xff0c;输入 : help tutor 就可以进入vim的教学       在 terminal 中输入 vim 文件名 就可以打开文件    vim有两种模式   normal mode &#xff08;普通模式&#xff09;→ 指令操作   insert mode &#xff08;输入模式&…...

【论文阅读】Robust Multi-Instance Learning with Stable Instances

1、摘要与引言 以往的MIL算法遵循i.i.d假设&#xff1a;训练样本与测试样本都分别来自于同一分布中&#xff0c;而这一假设往往与现实应用中有所出入。研究人员通过计算训练样本与测试样本之间的密度比对训练样本进行加权&#xff0c;以解决分布变化带来的问题。 分布的变化发…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...