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

【sentinel】熔断降级规则详解及源码分析

概述

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方API等。例如,支付的时候,可能需要远程调用银联提供的API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

在这里插入图片描述

现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

Sentinel在1.8.0版本对熔断降级做了大的调整,可以定义任意时长的熔断时间,参照Hystrix引入了半开启恢复的支持。

熔断状态有以下三种:

状态说明
OPEN熔断开启状态,拒绝所有请求
HALF_OPEN半开状态,如果接下来的一个请求顺利通过则表示结束熔断,否则继续熔断
CLOSE熔断关闭状态,请求顺利通过

熔断状态参考枚举类com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State

/*** Circuit breaker state.*/
enum State {/*** In {@code OPEN} state, all requests will be rejected until the next recovery time point.*/OPEN,/*** In {@code HALF_OPEN} state, the circuit breaker will allow a "probe" invocation.* If the invocation is abnormal according to the strategy (e.g. it's slow), the circuit breaker* will re-transform to the {@code OPEN} state and wait for the next recovery time point;* otherwise the resource will be regarded as "recovered" and the circuit breaker* will cease cutting off requests and transform to {@code CLOSED} state.*/HALF_OPEN,/*** In {@code CLOSED} state, all requests are permitted. When current metric value exceeds the threshold,* the circuit breaker will transform to {@code OPEN} state.*/CLOSED
}

在这里插入图片描述

熔断降级规则说明

熔断降级规则(DegradeRule)包含下面几个重要的属性:

Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

在这里插入图片描述

熔断策略

慢调用比例

慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用RT(Response Time),即最大的响应时间,请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断,熔断状态变为OPEN。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用RT则结束熔断(CLOSED),若大于设置的慢调用RT则会再次被熔断(OPEN)。

举个例子:针对资源/sentinel/slowRatio进行降级,

@RequestMapping("/slowRatio")
public String slowRatio(Integer num) throws InterruptedException {// 模拟慢请求TimeUnit.SECONDS.sleep(5);return String.valueOf(10 / num);
}

降级规则配置如下:

在这里插入图片描述

熔断效果如下:

在这里插入图片描述

异常比例

异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是01,代表0%100%。

注意异常降级仅针对业务异常,对Sentinel限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex)记录业务异常。

示例:

Entry entry = null;
try {entry = SphU.entry(resource);// Write your biz code here.// <<BIZ CODE>>
} catch (Throwable t) {if (!BlockException.isBlockException(t)) {Tracer.trace(t);}
} finally {if (entry != null) {entry.exit();}
}

如果使用了sentinel的适配器模块,如Sentinel Dubbo Adapter, Sentinel Web Servlet Filter或@SentinelResource注解会自动统计业务异常,无需手动调用。

异常数

异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

熔断器事件监听

Sentinel支持注册自定义的事件监听器监听熔断器状态变换事件(state change event)。

示例:

@PostConstruct
public void init() {EventObserverRegistry.getInstance().addStateChangeObserver("logging",(prevState, newState, rule, snapshotValue) -> {// 变换至 OPEN state 时会携带触发时的值log.error("{} -> {}, snapshotValue={}", prevState, newState, snapshotValue);});
}

监听上面的规则会打印如下的日志,可以从日志中看到中间会熔断5s,5s过后放一个请求进来,状态变为HALF_OPEN,这一个请求耗时500ms,超过阈值,又触发了熔断。

2023-03-25 10:56:28.422 ERROR 16296 --- [io-8030-exec-16] c.m.user.controller.DegradeController    : CLOSED -> OPEN, snapshotValue=1.0
2023-03-25 11:00:53.279 ERROR 16296 --- [nio-8030-exec-8] c.m.user.controller.DegradeController    : OPEN -> HALF_OPEN, snapshotValue=null
2023-03-25 11:00:53.839 ERROR 16296 --- [nio-8030-exec-8] c.m.user.controller.DegradeController    : HALF_OPEN -> OPEN, snapshotValue=1.0
2023-03-25 11:00:58.879 ERROR 16296 --- [nio-8030-exec-8] c.m.user.controller.DegradeController    : OPEN -> HALF_OPEN, snapshotValue=null

源码分析

DegradeSlot

Sentinel的熔断是由责任链中的最后一个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 {// 在触发后续slot前执行熔断的检查performChecking(context, resourceWrapper);fireEntry(context, resourceWrapper, node, count, prioritized, args);}void performChecking(Context context, ResourceWrapper r) throws BlockException {// 根据资源名称查找断路器CircuitBreakerList<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {return;}// 遍历所有的CircuitBreaker,判断是否让这个请求通过for (CircuitBreaker cb : circuitBreakers) {// tryPass里面只做了状态检查,熔断是否关闭或者打开/*** @see AbstractCircuitBreaker#tryPass(com.alibaba.csp.sentinel.context.Context)*/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();//如果当前其他slot已经有了BlockException直接调用fireExit,不用继续走熔断逻辑了if (curEntry.getBlockError() != null) {fireExit(context, r, count, args);return;}// 根据资源名称查找断路器CircuitBreakerList<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {fireExit(context, r, count, args);return;}if (curEntry.getBlockError() == null) {// passed request//调用CircuitBreaker的onRequestComplete()方法for (CircuitBreaker circuitBreaker : circuitBreakers) {circuitBreaker.onRequestComplete(context);}}fireExit(context, r, count, args);}
}

进入DegradeSlot时,只会检查断路器是否已经打开,再根据是否超过了重试时间来开启半开状态,然后就直接返回是否通过。

而真正判断是否需要开启断路器的地方时在exit()方法里面,因为这个方法是在业务方法执行后调用的,只有在业务方法执行完成后,断路器才能收集到业务异常或者业务方法的执行时间来判断是否打开断路器。

先来看进入DegradeSlot的entry()方法,这里调用了CircuitBreaker.tryPass()方法,CircuitBreaker有ExceptionCircuitBreaker和ResponseTimeCircuitBreaker两种类型的断路器,CircuitBreaker继承关系图如下:

在这里插入图片描述

AbstractCircuitBreaker

entry()方法实际上调用了AbstractCircuitBreaker.tryPass()方法,这里只做了一个处理,如果断路器开启,但是上一个请求距离现在已经过了重试间隔时间就开启半启动状态。

public boolean tryPass(Context context) {// Template implementation.if (currentState.get() == State.CLOSED) {// 关闭状态,返回true,让请求通过return true;}if (currentState.get() == State.OPEN) {// For half-open state we allow a request for probing.// 如果断路器开启,但是上一个请求距离现在已经过了重试间隔时间就开启半开状态return retryTimeoutArrived() && fromOpenToHalfOpen(context);}// 半开状态,直接返回false,拒绝请求return false;
}protected boolean retryTimeoutArrived() {// 判断当前时间是否超过重试时间return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
}protected boolean fromOpenToHalfOpen(Context context) {// OPEN -> HALF_OPENif (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {// 通知观察者// 可通过EventObserverRegistry.getInstance().addStateChangeObserver注册观察者监听状态的变化notifyObservers(State.OPEN, State.HALF_OPEN, null);Entry entry = context.getCurEntry();entry.whenTerminate(new BiConsumer<Context, Entry>() {@Overridepublic void accept(Context context, Entry entry) {// Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638// Without the hook, the circuit breaker won't recover from half-open state in some circumstances// when the request is actually blocked by upcoming rules (not only degrade rules).if (entry.getBlockError() != null) {// Fallback to OPEN due to detecting request is blocked// 如果异常了// HALF_OPEN -> OPENcurrentState.compareAndSet(State.HALF_OPEN, State.OPEN);notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);}}});return true;}return false;
}

ResponseTimeCircuitBreaker

exit()方法调用了ExceptionCircuitBreaker和ResponseTimeCircuitBreaker的onRequestComplete()方法。

ResponseTimeCircuitBreaker

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();if (rt > maxAllowedRt) {// 请求响应时间>最大响应时间// 慢调用数+1counter.slowCount.add(1);}// 总的请求+1counter.totalCount.add(1);handleStateChangeWhenThresholdExceeded(rt);
}private void handleStateChangeWhenThresholdExceeded(long rt) {if (currentState.get() == State.OPEN) {// OPEN 直接返回,已经有其他请求触发熔断降级了return;}if (currentState.get() == State.HALF_OPEN) {// In detecting request// TODO: improve logic for half-open recovery// HALF_OPEN放了一个请求进来if (rt > maxAllowedRt) {// HALF_OPEN -> OPENfromHalfOpenToOpen(1.0d);} else {// HALF_OPEN -> CLOSEfromHalfOpenToClose();}return;}List<SlowRequestCounter> counters = slidingCounter.values();long slowCount = 0; // 慢请求数long totalCount = 0; // 总请求数for (SlowRequestCounter counter : counters) {slowCount += counter.slowCount.sum();totalCount += counter.totalCount.sum();}if (totalCount < minRequestAmount) {// 总请求数小于最小请求数,直接返回,不熔断return;}double currentRatio = slowCount * 1.0d / totalCount;if (currentRatio > maxSlowRequestRatio) {// 当前慢请求比例 > 最大慢请求比例transformToOpen(currentRatio);}if (Double.compare(currentRatio, maxSlowRequestRatio) == 0 &&Double.compare(maxSlowRequestRatio, SLOW_REQUEST_RATIO_MAX_VALUE) == 0) {// 当前慢请求比例 = 最大慢请求比例 = 1.0transformToOpen(currentRatio);}
}

ExceptionCircuitBreaker

com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker#onRequestComplete

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) {// 异常数+1counter.getErrorCount().add(1);}// 总请求数+1counter.getTotalCount().add(1);// 熔断逻辑,处理熔断状态的变更handleStateChangeWhenThresholdExceeded(error);
}private void handleStateChangeWhenThresholdExceeded(Throwable error) {if (currentState.get() == State.OPEN) {// OPEN 直接返回,已经有其他请求触发熔断降级了return;}if (currentState.get() == State.HALF_OPEN) {// HALF_OPEN放了一个请求进来// In detecting requestif (error == null) {// HALF_OPEN -> CLOSEfromHalfOpenToClose();} else {// HALF_OPEN -> OPENfromHalfOpenToOpen(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();}if (totalCount < minRequestAmount) {// 总请求数小于最小请求数 直接返回return;}double curCount = errCount;if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {// Use errorRatio// 计算异常比例curCount = errCount * 1.0d / totalCount;}// 异常数大于阈值// 异常比例大于阈值if (curCount > threshold) {// CLOSE -> OPENtransformToOpen(curCount);}
}

相关文章:

【sentinel】熔断降级规则详解及源码分析

概述 除了流量控制以外&#xff0c;对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块&#xff0c;可能是另外的一个远程服务、数据库&#xff0c;或者第三方API等。例如&#xff0c;支付的时候&#xff0c;可能需要远程调用银联…...

ffplay源码分析-main函数入口分析

ffplay源码分析-main函数入口分析 基于ffmpeg6.0源码分析。 流程 使用ffplay播放视频文件&#xff0c;会触发main函数的调用。main函数中会进行以下操作&#xff1a; 从命令行中解析日志级别、日志是否需要落文件、是否要输出banner信息。banner信息包含版权、库的版本。注…...

C++三种继承方式

C继承的一般语法为&#xff1a;class 派生类名:&#xff3b;继承方式&#xff3d; 基类名{派生类新增加的成员};继承方式限定了基类成员在派生类中的访问权限&#xff0c;包括 public&#xff08;公有的&#xff09;、private&#xff08;私有的&#xff09;和 protected&#…...

【Android -- 软技能】《软技能:代码之外的生存指南》之好书推荐(一)

前言 这是一本由美国的一个软件开发人员写的&#xff0c;但书中除了有 Java 、C# 几个单词外&#xff0c;没有一行代码。 因为这本书讲的是代码之外的东西。 文章目录结构&#xff1a; 1. 职业 从业心态&#xff1a;说白了就是要有责任心&#xff0c;把每份工作要当成是自…...

Nginx可视化管理工具 - Nginx Proxy Manager

一、介绍 nginx-proxy-manager 是一个反向代理管理系统,它基于Nginx,具有漂亮干净的 Web UI。还可以获得受信任的 SSL 证书,并通过单独的配置、自定义和入侵保护来管理多个代理。 其官网地址如下: https://nginxproxymanager.com/ 二、安装 第一步:192.168.1.108服务…...

https是如何保证安全的

在学习http与https的区别的时候&#xff0c;我们通常从以下几点出发&#xff1a;http是超文本传输协议&#xff0c;是明文传输&#xff0c;有安全风险&#xff0c;https在TCP和http网络层之间加入了SSL/TLS安全协议&#xff0c;使得报文能够加密传输http连接简单&#xff0c;三…...

ubuntu下使用GCC开发单片机的过程

以下是一个简单的单片机C程序示例,实现的功能是控制LED灯的闪烁: #include <reg52.h> // 导入单片机的寄存器定义void main() {while(1) { // 无限循环P1 = 0x00; // P1口输出低电平delay(1000); // 延时1秒P1 = 0xff; // P1口输出高电平delay(1000); // 延时1秒…...

人工智能能否取代软硬件开发工程师

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 人工智能发展趋势 随着AI技术的不断发展&#xff0c;它正在改变我们的生活方式、商业模式和工作方式。人工智能技术的发展一直处于快速变化和持续创新的状态&#xff0c;以下…...

BPI-R3开发板 - uboot编译

一. 获取源码 https://github.com/mtk-openwrt/u-boot 二. 编译步骤 编译环境为ubuntu 18.04。交叉编译工具链我用的是openwrt编译生成的工具链&#xff0c;并设置到环境变量&#xff0c;如下&#xff1a; export PATH$PATH:/root/mt8976/BPI-R3-OPENWRT-V21.02.3-main/staging…...

优秀程序员的5个特征,你在第几层?

每个人程序员都对未来的职业发展有着憧憬和规划&#xff0c;要做架构师、要做技术总监、要做CTO。但现实总是复杂的&#xff0c;日复一日的工作与生活总能让人一次又一次地陷入迷茫。大部分原因就是对职业发展轨迹和自我能力提升的一般规律缺乏认识&#xff0c;做事找不到方向或…...

JAVA Session会话 Thymeleaf - 视图模板技术配置步骤

JAVAWebSession会话会话跟踪技术session保存作用域Thymeleaf - 视图模板技术配置过程Session会话 HTTP是无状态的&#xff1a;服务器无法区分这两个请求是同一个客户端发过来的&#xff0c;还是不同的客户端发过来的 现实问题&#xff1a;第一次请求是添加商品到购物车&#x…...

Linux编译cpprestsdk库

本文用的Linux系统为Ubuntu22.04&#xff0c;自带GCC11.3.0。 依赖 ①编译需要boost库&#xff0c;本文用的库版本为boost-1.82.0.beta1.tar.gz。 ②编译需要openssl库&#xff0c;这里使用的版本为openssl-1.1.1s.tar.gz。 ③编译需要cmake库&#xff0c;本文使用的是cmake-3…...

算法的时间复杂度和空间复杂度

目录 1 如何衡量一个算法的好坏 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见代码举例 2.3.1 Func2 O(N) 2.3.2 Func3 O(MN) 2.3.3 Func4 O(1) 2.3.4 Func5 strchr O(N) 2.3.5 Func6 冒泡排序 O(N^2) 2.3.6 Func7 二分…...

基本认识vue3

一、基本搭建 项目搭建 使用 最新的 Vue3 TS Vite项目 执行命令 &#xff08;本项目采用如下方式&#xff09; npm create vitelatest my-vite-app --template vue-ts或者 运行项目 npm install npm run dev项目搭建初始化目录 新搭建的项目可能会遇到个问题&#xf…...

HTTP/HTTPS协议认识

写在前面 这个博客我们要要讨论的是协议,主要是应用层.今天我们将正式认识HTTP和HTTPS,也要认识序列化和反序列化,内容比较多,但是不难 再谈协议 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层,我们要完成下面三个步骤. sock的使用 定制…...

【VScode】远程连接Linux

目录标题1. 安装扩展插件2. 在Linux上操作3. 确定Linux的IP地址4. 远程连接到Linux5. 实现免密码登录使用 VScode 远程编程与调试的时有会用到插件 Remote Development&#xff0c;使用这个插件可以在很多情况下代替 vim 直接远程修改与调试服务器上的代码&#xff0c;同时具备…...

QT/C++调试技巧:内存泄漏检测

文章目录内存泄漏方案一方案二&#xff1a;CRT调试定位代码位置方法1方法2其它问题方案三&#xff1a;使用vs诊断工具方案四&#xff1a;使用工具VLD&#xff08;Visio Leak Detector&#xff09;方案五Cppcheck内存泄漏 内存泄漏&#xff1a;指的是在程序里动态申请的内存在使…...

【贪心算法】一文让你学会“贪心”(贪心算法详解及经典案例)

文章目录前言如何理解“贪心算法”&#xff1f;贪心算法实战分析1.分糖果2.钱币找零3.区间覆盖内容小结最后说一句&#x1f431;‍&#x1f409;作者简介&#xff1a;大家好&#xff0c;我是黑洞晓威&#xff0c;一名大二学生&#xff0c;希望和大家一起进步。 &#x1f47f;本…...

【字体图标iconfont】字体图标部署流程+项目源码分析

今日&#xff0c;心情甚是烦闷&#xff0c;原由… 公司项目需要将字体图标做一些细微的调整&#xff0c;我一人分析了许久&#xff0c;看不大懂源码的逻辑&#xff0c;产生了自我怀疑。深吸一口气&#xff0c;重新鼓起勇气&#xff0c;调整心境&#xff0c;一下子豁然开朗&…...

2023最全的Web自动化测试介绍(建议收藏)

做测试的同学们都了解&#xff0c;做Web自动化&#xff0c;我们主要用Selenium或者是QTP。 有的人可能就会说&#xff0c;我没这个Java基础&#xff0c;没有Selenium基础&#xff0c;能行吗&#xff1f;测试虽然属于计算机行业&#xff0c;但其实并不需要太深入的编程知识&…...

jvm_根节点枚举安全点安全区域

1、可达性分析可以分成两个阶段 根节点枚举 从根节点开始遍历对象图 前文我们在介绍垃圾收集算法的时候&#xff0c;简单提到过&#xff1a;标记-整理算法(Mark-Compact)中的移动存活对象操作是一种极为负重的操作&#xff0c;必须全程暂停用户应用程序才能进行&#xff0c;像这…...

fabric(token-erc-20链码部署)

确保自己已经安装了fabric。没有安装的可以参考我之前的教程fabric中bootstrap.sh到底帮助我们干了什么&#xff1f;&#xff08;手动执行相关操作安装fabric2.4&#xff09;_./bootstrap.sh_小小小小关同学的博客-CSDN博客小伙伴们在跟着官方示例来安装fabric的时候都是相当烦…...

C语言基础——流程控制语句

文章目录一、流程控制语句 -- 控制程序的运行过程 9条&#xff08;一&#xff09;、条件选择流程控制语句&#xff1a;if语句if……else……语句if……else if……语句switch语句&#xff08;二&#xff09;、循环流程控制语句&#xff1a;for语句while语句do while……语句co…...

WinForm | C# 界面弹出消息通知栏 (仿Win10系统通知栏)

ApeForms 弹出消息通知栏功能 文章目录ApeForms 弹出消息通知栏功能前言全局API通知栏起始方向通知排列方向通知栏之间的间隔距离无鼠标悬停时的不透明度消息通知窗体的默认大小示例代码文本消息提示栏文本消息提示栏&#xff08;带选项&#xff09;图文消息提示栏图文消息提示…...

刷题之最长公共/上升子序列问题

目录 一、最长公共子序列问题&#xff08;LCS&#xff09; 1、题目 2、题目解读 ​编辑 3、代码 四、多写一题 五、应用 二、最长上升子序列问题&#xff08;LIS&#xff09; 1、题目 2、题目解读 3、代码 四、多写一道 Ⅰ、题目解读 Ⅱ、代码 一、最长公共子序列问题&…...

【数据结构】千字深入浅出讲解栈(附原码 | 超详解)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;C语言实现数据结构 &#x1f4ac;总结&#xff1a;希望你看完…...

自动驾驶V2X

1 SoC MDM9250 2 设备网络节点 mhi_swip0 rmnet_mhi0 3 网络协议栈log打印控制 include/linux/netdevice.h ethtool -s eth0 msglvl [level] ethtool -s eth0 msglvl 0x6001 4 URLs MHI initial design review https://lore.kernel.org/lkml/001601d52148$bd852840$388f78c0$c…...

零基础自学网络安全/渗透测试有哪些常见误区?

一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;且过渡到网络安全用到编程的用到的编程的关键点不多。一般人如果想要把编程学好再开始学习网络安全往…...

ConvMixer:Patches Are All You Need

Patches Are All You Need 发表时间&#xff1a;[Submitted on 24 Jan 2022]&#xff1b; 发表期刊/会议&#xff1a;Computer Vision and Pattern Recognition&#xff1b; 论文地址&#xff1a;https://arxiv.org/abs/2201.09792&#xff1b; 代码地址&#xff1a;https:…...

day10—编程题

文章目录1.第一题1.1题目1.2思路1.3解题2.第二题2.1题目2.2涉及的相关知识2.3思路2.4解题1.第一题 1.1题目 描述&#xff1a; 给定一个二维数组board&#xff0c;代表棋盘&#xff0c;其中元素为1的代表是当前玩家的棋子&#xff0c;0表示没有棋子&#xff0c;-1代表是对方玩…...

nodejs做静态网站/沧州网络推广外包公司

资深老师教你用CAD制作室内设计3D效果图 一、导入CAD平面图 在效果图制作中&#xff0c;经常会先导入CAD平面图&#xff0c;再根据导入的平面图的准确尺寸在3ds中建立造型。DWG格式是标准的AutoCAD绘图格式。单击菜单栏中的&#xff08;文件&#xff09;——&#xff08;输出…...

武汉大学人民医院东院官网/便宜的seo网络营销推广

转载于:https://www.cnblogs.com/taosha/p/6399663.html...

专卖二手手表网站/今日重要新闻

友元函数可以访问与其有好友关系类对象的数据成员&#xff0c;但不属于类的成员函数&#xff0c;因而没有this指针不能直接使用类的数据成员&#xff0c;需要利用引用来创建一个类的对象&#xff0c;才可以使用数据成员 代码说明如下 一个函数为类的友元函数 1 #include <io…...

四川建设厅网站查询/苏州百度 seo

ACCESS可在菜单上选择[压缩]来压缩当前正打开的数据库&#xff0c;如是我们就可以通过程序临时添加一个工具条按钮&#xff0c;然后使用 SendKeys 来模拟键盘操作&#xff0c;实现压缩当前数据库&#xff0c;压缩后它会重新打开。函数如下&#xff1a;Function Compact() 添加一…...

网站建设 用英语/长沙网站设计拓谋网络

是一个元素有一个状态变成另一个状态的css动画效果 transition:css的一个属性 时间&#xff0c;一个属性 时间&#xff0c;一个属性 时间。。。。&#xff1b; 在刚开始的时候要设置它初始的那个属性&#xff0c;然后在一段时间内属性变成什么样的。 比如.a{color:red;backgr…...

网站模块有哪些/seo快速排名软件app

OLTP系统的后端关系数据库用于存储不同种类的数据&#xff0c;理论上来讲&#xff0c;数据库中每一列的值都有其所代表的特定含义&#xff0c;数据也应该在存入数据库之前进行规范化处理&#xff0c;比如说“age”列&#xff0c;用于存储人的年龄&#xff0c;设置的数据类型为I…...