spring cloud之集成sentinel
写在前面
源码 。
本文一起看下spring cloud的sentinel组件的使用。
1:准备
1.1:理论
对于一个系统来说,最重要的就是高可用
,那么如何实现高可用呢?你可能会说,集群部署不就可以了,但事实并非这么简单,假定一个不那么靠谱的开发,写了一个不那么靠谱的sql语句,上线之后,DB资源很快耗尽,那么所有需要查询DB的服务都无法处理请求,进而导致这些服务挂掉,而上游的服务因为无法快速得到响应,也会很快的挂掉,进而整个系统都会无法对外提供服务,这其实就是发生了非常可怕的服务雪崩
,这个过程可以简单参考下图:
假定那个不那么靠谱的开发,是在服务D开发了那个不那么靠谱的sql语句,则上线后的后果就是D导致B和C不可用,B和C的不可用又会导致A的不可用,最终整个系统不可用。所以想要真正的实现高可用,除了多节点的集群部署外,我们还需要预防服务雪崩,而本文要学习的sentinel正是这样的一个组件,我们可以叫做容错组件
。
基本上,导致服务雪崩发生的原因在2个方面,第一个方面是高并发导致的请求量增大,第二方面就是应用内部的异常和错误(就像那个不那么靠谱的sql)
。其实sentinel也正是从这两方面来预防服务雪崩的,对于高并发,sentinel可以从外部来限制并发量,对于应用内部异常和错误,sentinel可以进行降级和熔断。我们也会从这方面来展开sentinel的实战环节。
在java中,万物皆对象,在Linux中,万物皆文件,而在sentinel中,万物皆资源,而这里的资源我们可以认为就是接口地址,而对这些资源作用的过程是通过其内部的一个责任链,可以参考下图:
比如有用来收集数据的StaticSlot,构建调用链的NodeSelectorSlot(形成一个树状的调用关系图)
,如果业务有需要,我们也可以增加自定义的额slot到这个调用链上。
1.2:安装sentinel
首先在这里 下载可运行的jar包,接着如下操作:
$ java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
INFO: Sentinel log output type is: file
INFO: Sentinel log charset is: utf-8
INFO: Sentinel log base directory is: C:\Users\dell9020\logs\csp\
INFO: Sentinel log name use pid is: false. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.0.5.RELEASE)...
成功后访问http://localhost:8080/#/login
,进入登陆页面:
账号sentinel/sentinel,成功后进入如下页面:
2:限流实战
限流,或者叫流量整形,是sentinel从外部预防服务雪崩的重要手段。
2.1:基础配置
这部分我们直接来使用sentiniel对我们项目做流量整形,首先,在custom和template模块引入依赖:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
然后在custom和template模块的application.yml中配置sentinel信息:
spring:jpa:...cloud:sentinel:eager: true transport:port: 8719dashboard: localhost:8080
接着使用@SentinelResource
注解标记要进行流量整形的接口,如下:
- custom模块
dongshi.daddy.sentinel.controller.CouponCustomerController#requestCoupon
@PostMapping("requestCoupon")
@SentinelResource(value = "requestCoupon")
public Coupon requestCoupon(@Valid @RequestBody RequestCoupon request) {...
}
@PostMapping("findCoupon")
@SentinelResource(value = "customer-findCoupon")
public List<CouponInfo> findCoupon(@Valid @RequestBody SearchCoupon request) {return customerService.findCoupon(request);
}
- template模块
// 读取优惠券
@GetMapping("/getTemplate")
@SentinelResource(value = "getTemplate")
public CouponTemplateInfo getTemplate(@RequestParam("id") Long id){log.info("Load template, id={}", id);/*try {// 休眠二十秒模拟超时TimeUnit.SECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}*/return couponTemplateService.loadTemplateInfo(id);
}// 批量获取
@GetMapping("/getBatch")
// 降级要执行的方法
@SentinelResource(value = "getTemplateInBatch", blockHandler = "getTemplateInBatch_block")
public Map<Long, CouponTemplateInfo> getTemplateInBatch(@RequestParam("ids") Collection<Long> ids) {log.info("getTemplateInBatch: {}", JSON.toJSONString(ids));return couponTemplateService.getTemplateInfoMap(ids);
}
// 降级要执行的方法
public Map<Long, CouponTemplateInfo> getTemplateInBatch_block(Collection<Long> ids, BlockException exception) {log.info("接口被限流");return Maps.newHashMap();
}
接着我们启动服务,如果一切正常的话,就可以看到custom和template这两个模块注册到sentinel上了:
2.2:通过流控规则进行限流
sentinel支持通过三种方式来进行流量整形:
直接流控:针对特定接口(sentinel称为资源)限流
关联流控:当某接口(sentinel称为资源)达到限流条件时对关联的接口(sentinel称为资源)进行限流
链路流控:
2.2.1:直接流控
我们对资源getTemplateInBatch
进行流控,在sentinel选择template对应的微服务,点击子菜单流控规则
,然后点击右上角 添加流控规则,如下:
为了测试我们使用jmeter来进行测试,jmeter配置可以从这里 下载,配置如下:
通过按钮 启动,之后查看template模块日志输出如下:
2024-01-08 15:34:44.411 INFO 26504 --- [io-20006-exec-7] d.d.s.c.CouponTemplateController : getTemplateInBatch: [2,3]
Hibernate: select coupontemp0_.id as id1_0_, coupontemp0_.available as availabl2_0_, coupontemp0_.type as type3_0_, coupontemp0_.created_time as created_4_0_, coupontemp0_.description as descript5_0_, coupontemp0_.name as name6_0_, coupontemp0_.rule as rule7_0_, coupontemp0_.shop_id as shop_id8_0_ from coupon_template coupontemp0_ where coupontemp0_.id in (? , ?)
2024-01-08 15:34:44.904 INFO 26504 --- [io-20006-exec-5] d.d.s.c.CouponTemplateController : 接口被限流
可以看到第二次执行了熔断逻辑被限流了。
2.2.2:关联流控
jmeter配置下载 。
我们来设置当getTempalte接口qps超过1时限制getBatch接口,配置如下:
首先我们使用jmeter模拟qps 2不间断访问getTemplate接口,如下:
启动后,这样,肯定就会触发限流规则了,这样,我们先来启动jmeter,
之后访问getBatch接口,如下:
http://localhost:20006/template/getBatch?ids=2,3
查看后台输出:
然后我们来停止jmeter,再来请求接口,就会恢复正常了:
2.2.3:链路流控
jmeter配置下载 。
对于同一个资源访问可以来自于不同的链路,针对特定的链路进行流控,就是链路流控了,如下图,上方链路就是被流控的:
这里我们模拟从custom模块访问template模块的getBatch接口,为了测试,需要首先在custom模块中增加如下接口:
// 批量获取
@GetMapping("/getBatchFromCustomer")
public Map<Long, CouponTemplateInfo> getTemplateInBatch(@RequestParam("ids") Collection<Long> ids) {log.info("getTemplateInBatch111: {}", JSON.toJSONString(ids));return templateService.getTemplateInBatch(ids);
}
因为需要进行限流的资源需要知道当前的调用者是谁,才能知道是否需要触发,所以,首先需要在custom模块添加openfeign的拦截器,在openfeign的请求中添加来源头
:
@Configuration
public class OpenfeignSentinelInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {template.header("SentinelSource", "coupon-customer-serv");}
}
接着在tempalte模块中获取这个来源的信息:
@Component
@Slf4j
public class SentinelOriginParser implements RequestOriginParser {@Overridepublic String parseOrigin(HttpServletRequest request) {log.info("request {}, header={}", request.getParameterMap(), request.getHeaderNames());return request.getHeader("SentinelSource");}
}
接着我们来配置sentinel针对来源coupon-customer-serv
进行限流,如下:
接着开启jmeter,正常的话会看到被限流了:
为了测试来源不匹配的情况不限流,我们将sentinel的目标资源名称随便改为其他的,如下:
此时再开启jmeter测试就正常了:
2.3:三种流控效果
sentinel支持三种流控效果,快速失败,warm up,排队等待,在上述的实战环节我们使用就是快速失败,这也是sentinel默认的流控效果。
- 快速失败
直接失败。 - warm up
这是一种从低水位逐渐拉高的一种限流方式,比如如下的配置:
上图设置在5秒内拉高到10进行限流,而非开始就是10,开始的限流值需要除以冷却因子,这里冷却因子是3,所以就是10/3=3,也就是当qps到达3时开始限流,然后在5秒内将这个限流值拉高到最大值10,
使用的场景,如采用缓存+DB读的场景,如果接口一段时间内都处于很低的水位,导致大量的缓存都失效了,此时突然发生了突发流量(某明星出轨了,某漂亮country被原子弹攻击了)
,此时缓存还没有完全构建起来,为了避免突发流量全部打到DB,把DB打穿,就可以考虑使用warm up,在一段时间内从低水位逐渐拉到高水位,同时在这段时间内完成缓存的构建工作。
- 排队等待
被限流的请求在一个任务队列中排队等待,并按照超时等待时间从队列中删除任务,如下500内还没有处理则移除任务的配置:
2:降级、熔断实战
jmeter配置 。
降级、熔断,是sentinel从服务内部预防服务雪崩的重要手段。这部分我们一起来看下如何使用sentinel来实现降级和熔断。为了方便测试我们首先在template模块添加一个新的接口并设置为sentinel的资源:
// 批量获取
@GetMapping("/getBatchV1")
// 降级要执行的方法
@SentinelResource(value = "getTemplateInBatchV1", fallback = "getTemplateInBatch_fallback")
public Map<Long, CouponTemplateInfo> getTemplateInBatchV1(@RequestParam("ids") Collection<Long> ids) {log.info("getTemplateInBatch: {}", JSON.toJSONString(ids));int i = 1 / 0;return couponTemplateService.getTemplateInfoMap(ids);
}public Map<Long, CouponTemplateInfo> getTemplateInBatch_fallback(Collection<Long> ids) {log.info("接口被fallback");return Maps.newHashMap();
}
这里注意指定了fallback = "getTemplateInBatch_fallback"
,则当抛出了RuntimeException时就会进入执行降级逻辑,接着我们就需要在sentinel中配置熔断逻辑了,如下:
降级熔断的判断过程可以参考下图(注意时示例)
:
接着执行jemter,日志输出如下:
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : getTemplateInBatch: [2,3] #
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.813 INFO 13112 --- [o-20006-exec-10] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@7d50964c
2024-01-10 14:01:47.814 INFO 13112 --- [o-20006-exec-10] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.815 INFO 13112 --- [io-20006-exec-9] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@1f47105b
2024-01-10 14:01:47.816 INFO 13112 --- [io-20006-exec-9] d.d.s.c.CouponTemplateController : 接口被fallback
....
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : getTemplateInBatch: [2,3] # 1s后半开启,后又重新进入熔断
2024-01-10 14:01:47.813 INFO 13112 --- [io-20006-exec-2] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.813 INFO 13112 --- [o-20006-exec-10] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@7d50964c
2024-01-10 14:01:47.814 INFO 13112 --- [o-20006-exec-10] d.d.s.c.CouponTemplateController : 接口被fallback
2024-01-10 14:01:47.815 INFO 13112 --- [io-20006-exec-9] d.daddy.sentinel.SentinelOriginParser : request org.apache.catalina.util.ParameterMap@39d7b9e0, header=org.apache.tomcat.util.http.NamesEnumerator@1f47105b
2024-01-10 14:01:47.816 INFO 13112 --- [io-20006-exec-9] d.d.s.c.CouponTemplateController : 接口被fallback
...
可以看到降级后,满足条件就会进入熔断。一段时间后会尝试恢复,这个状态叫做半熔断,完整的状态转换参考下图:
4:接入nacos实现持久化
二次开发后源码 。
在前面的实战中,不知道你发现没有,如果是应用重启,则我们设置的规则就都会丢失,这是因为这些规则信息默认是保存在内存中,sentinel也支持将规则信息保存在nacos中,本部分就一起来看下如何实现,想要实现这个操作,需要对sentinel的源码做一下简单的二次开发工作。我们开始。
4.1:下载sentient源码
你可以在这里 下载源码,如下图:
下载后导入到idea中,注意将idea中jdk相关的配置都修改为1.8,否则可能会无法正常编译代码,成功后如下图:
接着就可以开始二次开发的工作了。
4.2:二次开发
我们需要修改的模块是sentinel-dashboard
,首先需要修改nacos依赖范围,默认是test的,即只在test阶段生效,我们需要修改为编译阶段的,如下:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><!-- 将scope注释掉,改为编译期打包 --><!--<scope>test</scope>-->
</dependency>
接着我们需要将test包下com.alibaba.csp.sentinel.dashboard.rule.nacos
的四个nacos操作的相关文件,如下:
NacosConfig:初始化 Nacos Config 的连接;(该类我们需要修改,设置自己环境的nacos地址)
NacosConfigUtil:约定了 Nacos 配置文件所属的 Group 和文件命名后缀等常量字段;
FlowRuleNacosProvider:从 Nacos Config 上获取限流规则;(从nacos获取配置数据)
FlowRuleNacosPublisher:将限流规则发布到 Nacos Config。(将配置写到nacos)
我们需要在main目录中创建一样的包路径,并将这四个类拷贝过去,如下图:
首先我们修改NacosConfig
类nacosConfigService方法,该方法负责创建操作nacos的ConfigService的bean,如下:
/*
负责更新以及读取nacos的类
*/
@Bean
public ConfigService nacosConfigService() throws Exception {// 将Nacos的注册地址引入进来Properties properties = new Properties();properties.setProperty("serverAddr", "192.168.10.77:8868");properties.setProperty("namespace", "dev");
// return ConfigFactory.createConfigService("localhost");return ConfigFactory.createConfigService(properties);
}
然后修改FlowControllerV2中写入sentinel配置的pulisher和读取配置的provider为nacos的,如下:
@Autowired
// @Qualifier("flowRuleDefaultProvider")
// 修改为nacos的数据provider提供者,这样就能从nacos中获取数据
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
// @Qualifier("flowRuleDefaultPublisher")
// 修改为nacos的发布者,这样就能将规则配置数据写到nacos中
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
我们来看下publisher向nacos写数据的源码:
// com.alibaba.csp.sentinel.dashboard.rule.nacos.FlowRuleNacosPublisher#publish
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}// app 应用名称// public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";// public static final String GROUP_ID = "SENTINEL_GROUP";// converter.convert(rules) 转成字符串configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, converter.convert(rules));
}
源码中就可以看到nacos作为配置中心时的组是SENTINEL_GROUP
,dataId是${spring.application.name}-flow-rules
。最后我们修改页面sidebar.html,增加如下内容:
<li ui-sref-active="active"><a ui-sref="dashboard.flow({app: entry.app})"><i class="glyphicon glyphicon-filter"></i> 流控规则 极客时间改造</a>
</li>
最后需要改造我们的微服务,以cusotm为例,首先增加依赖:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
然后增加配置:
spring:cloud:sentinel:datasource:# 数据源的key,可以自由命名geekbang-flow:# 指定当前数据源是nacosnacos:# 设置Nacos的连接地址、命名空间和Group IDserver-addr: localhost:8848namespace: devgroupId: SENTINEL_GROUP# 设置Nacos中配置文件的命名规则dataId: ${spring.application.name}-flow-rules# 必填的重要字段,指定当前规则类型是"限流"rule-type: flow
最后,打jar包,成功后会在target目录看到jar包:
最后启动项目:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
启动custom模块,登录后就可以看到新加的菜单:
如果是该菜单增加配置,则会同步到nacos中,并且配置dataID是coupon-customer-serv-sentinel-flow-rules:
配置内容是以文本的格式存储的,但存储的也是json:
写在后面
参考文章列表
jmeter之简单使用 。
相关文章:
spring cloud之集成sentinel
写在前面 源码 。 本文一起看下spring cloud的sentinel组件的使用。 1:准备 1.1:理论 对于一个系统来说,最重要的就是高可用,那么如何实现高可用呢?你可能会说,集群部署不就可以了,但事实并…...
让车辆做到“耳听八方”,毫米波雷达芯片与系统设计
摘要: 毫米波雷达,是指工作在毫米波波段(一般为30~300GHz频域,波长1~10mm)探测的雷达。毫米波雷达体积小、质量轻、空间分辨率高,穿透“雾烟灰”的能力强,还具备全天候全天时工作的优势。在智能网联汽车体系中,毫米波雷达是系统感知层不可或缺的重要硬件,能让智能驾…...
Python如何实现数据驱动的接口自动化测试
大家在接口测试的过程中,很多时候会用到对CSV的读取操作,本文主要说明Python3对CSV的写入和读取。下面话不多说了,来一起看看详细的介绍吧。 1、需求 某API,GET方法,token,mobile,email三个参数 token为必填项mobil…...
高级分布式系统-第15讲 分布式机器学习--联邦学习
高级分布式系统汇总:高级分布式系统目录汇总-CSDN博客 联邦学习 两种常见的架构:客户-服务器架构和对等网络架构 联邦学习在传统的分布式机器学习基础上的变化。 传统的分布式机器学习:在数据中心或计算集群中使用并行训练,因为…...
小程序基础学习(事件处理)
原理:组件内部设置点击事件,然后冒泡到页面捕获点击事件 在组件内部设置点击事件 处理点击事件,并告诉页面 页面捕获点击事件 页面处理点击事件 组件代码 <!--components/my-info/my-info.wxml--> <view class"title"…...
网络协议与攻击模拟_01winshark工具简介
一、TCP/IP协议簇 网络接口层(没有特定的协议) 物理层:PPPOE宽带拨号(应用场景:宽带拨号,运营商切网过来没有固定IP就需要拨号,家庭带宽一般都采用的是拨号方式)数据链路层网络层…...
【linux学习笔记】网络
目录 【linux学习笔记】网络检查、监测网络ping-向网络主机发送特殊数据包traceroute-跟踪网络数据包的传输路径netstat-检查网络设置及相关统计数据 通过网络传输文件ftp 【linux学习笔记】网络 检查、监测网络 ping-向网络主机发送特殊数据包 最基本的网络连接命令就是pin…...
JUC-线程中断机制和LockSupport
线程中断机制 概念 java提供了一种用于停止线程的协商机制-中断。称为中断标识协商机制。 常用API public void interrupt() 仅仅让线程的中断标志位设置为true。不进行其他操作。public boolean isInterrupted() 获取中断标志位的状态。public static boolean interrupted…...
哈希表与哈希算法(Python系列30)
在讲哈希表数据结构和哈希算法之前,我想先刨析一下数组和python中的列表 首先来讲一下数组,我想在这提出一个疑问: 为什么数组通过索引查询数据的时间复杂度为O(1),也就是不管数组有多大,算法的执行时间都是不变的。…...
『 C++ 』AVL树详解 ( 万字 )
🦈STL容器类型 在STL的容器中,分为几种容器: 序列式容器(Sequence Containers): 这些容器以线性顺序存储元素,保留了元素的插入顺序。 支持随机访问,因此可以使用索引或迭代器快速访问任何位置的元素。 主要的序列式…...
Python下载安装pip方法与步骤_pip国内镜像
前提:下载安装好 python 打开命令提示符winR->cmd(不需要进入 python,直接在终端输入指令执行即可,也可以再 pycharm 终端执行命令)加入要安装ipython,需要执行以下命令: pip install **<…...
自动化测试框架pytest系列之基础概念介绍(一)
如果你要打算学习自动化测试 ,无论是web自动化、app自动化还是接口自动化 ,在学习的道路上,你几乎会遇到pytest这个测试框架,因为自动化编写没有测试框架,根本玩不了 。 如果你已经是一位自动化测试人员 ,…...
编码技巧:如何在Golang中高效解析和生成XML
编码技巧:如何在Golang中高效解析和生成XML 引言Golang中的XML基础解析XML文件生成XML文件错误处理和调试高级技巧和最佳实践总结 引言 在当今数据驱动的编程世界中,有效地处理各种数据格式是每个开发人员必备的技能之一。其中,XMLÿ…...
24校招,帆书测试开发工程师一面
前言 樊高读书是帆书的前身,我之前还看过他们的书,缘分闭环了 时间:25min 平台:飞书视频面试 过程 自我介绍为啥从后端转测试?通过实习经历,对测试有什么了解?讲一下游戏测试经历负责什么业…...
Java 方法以及在计算机内部的调用问题
修饰符 返回值类型 方法名( 形参列表 ){ 方法体代码(需要执行的功能代码) return 返回值; } 方法在内种没有先后顺序,但是不能把一个方法定义在另一个方法中。 方法的返回值类型写void(无返回申明)时,方法内不能使用return返回数…...
【算法与数据结构】343、LeetCode整数拆分
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:博主做这道题的时候一直在思考,如何找到 k k k个正整数, k k k究竟为多少合适。…...
中级Python面试问题
文章目录 专栏导读1、xrange 和 range 函数有什么区别?2、什么是字典理解?举个例子3、元组理解吗?如果是,怎么做,如果不是,为什么?4、 列表和元组的区别?5、浅拷贝和深拷贝有什么区别…...
Lede(OpenWrt)安装和双宽带叠加
文章目录 一、Lede介绍1. 简介2. 相关网站 二、Lede安装1. 编译环境2. SHELL编译步骤3. 腾讯云自动化助手 三、Lede配置1. 电信接口配置2. 联通接口配置3. 多线多播配置4. 网速测试效果 一、Lede介绍 1. 简介 LEDE是一个专为路由器和嵌入式设备设计的自由和开源的操作系统。 …...
HTML+JS + layer.js +qrcode.min.js 实现二维码弹窗
HTMLJSVUE qrcode.min.js 实现二维码生成 引入qrcode.js创建二维码显示位置编写JS 引入qrcode.js <script type"text/javascript" src"https://static.runoob.com/assets/qrcode/qrcode.min.js"></script>创建二维码显示位置 id 作为 定位标识…...
leetcode 142 环形链表II
题目 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使…...
电阻表示方法和电路应用
电阻 电阻的表示方法 直标法 直标法是将电阻器的类别及主要技术参数的数值直接标注在电阻器表面上 通常用3位阿拉伯数字来标注片状电阻的阻值,其中第1位数代表阻值的第1位有效数;第2位数代表阻值的第二位有效数字;第3位数代表阻值倍率&…...
论文笔记(三十九)Learning Human-to-Robot Handovers from Point Clouds
Learning Human-to-Robot Handovers from Point Clouds 文章概括摘要1. 介绍2. 相关工作3. 背景3.1. 强化学习3.2. 移交模拟基准 4. 方法4.1. Handover Environment4.2. 感知4.3. 基于视觉的控制4.4. 师生两阶段培训 (Two-Stage Teacher-Student Training) 5. 实验5.1. 模拟评估…...
浅学Linux之旅 day2 Linux系统及系统安装介绍
答案在时间,耐心是生活的关键 ——24.1.15 一、Linux系统介绍 林纳斯.托瓦兹在1991年开发了Linux内核(开源免费) Linux系统组成 Linux内核 系统库 系统程序 Linux内核和Linux发行版 Linux内核 -> 开源免费,林纳斯开发 Linux发行…...
探索未来餐饮:构建创新连锁餐饮系统的技术之旅
随着数字化时代的发展,连锁餐饮系统的设计和开发不再仅仅关乎订单处理,更是一场充满技术创新的冒险。在本文中,我们将深入研究连锁餐饮系统的技术实现,带你探索未来餐饮业的数字化美食之旅。 1. 构建强大的后端服务 在设计连锁…...
Unity组件开发--AB包打包工具
1.项目工程路径下创建文件夹:ABundles 2.AB包打包脚本: using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement;public class AssetBundle…...
毕业设计:基于python微博舆情分析系统+可视化+Django框架 K-means聚类算法(源码)✅
毕业设计:2023-2024年计算机专业毕业设计选题汇总(建议收藏) 毕业设计:2023-2024年最新最全计算机专业毕设选题推荐汇总 🍅感兴趣的可以先收藏起来,点赞、关注不迷路,大家在毕设选题ÿ…...
xbox如何提升下载速度?
提高Xbox的下载速度可以通过以下几种方法: 连接稳定的网络:使用有线以太网连接而不是无线连接,因为有线连接通常更稳定且速度更快。 关闭正在运行的游戏和应用程序:运行游戏或应用程序会消耗网络资源和处理能力,关闭它…...
day13 滑动窗口最大值 前K个高频元素
题目1:239 滑动窗口最大值 题目链接:239 滑动窗口最大值 题意 长度为K的滑动窗口从整数数组的最左侧移动到最右侧,每次只移动1位,求滑动窗口中的最大值 不能使用优先级队列,如果使用大顶堆,最终要pop的…...
Unity——VContainer的依赖注入
一、IOC控制反转和DI依赖倒置 1、IOC框架核心原理是依赖倒置原则 C#设计模式的六大原则 使用这种思想方式,可以让我们无需关心对象的生成方式,只需要告诉容器我需要的对象即可,而告诉容器我需要对象的方式就叫做DI(依赖注入&…...
【面试突击】Spring 面试实战
🌈🌈🌈🌈🌈🌈🌈🌈 欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理…...
公司做影视网站侵权/百度seo霸屏软件
原标题:iOS便签如何实现扫描二维码界面功能这是一款多功能便签软件工具,有“二维码”功能:它支持用户将便签内容制作成二维码,然后通过敬业签app扫描该二维码,显示相应的便签内容。那么,如何扫描便签二维码…...
西宁网站制作费用是多少钱/黄山seo公司
记录学习,不进行正文展示 目录 1、pom.xml 2、拦截器 3、RestController 5、SpringBoot启动类 6、application.properties 7、测试 1、pom.xml <!--springBoot工程--><groupId>com.qinluyu</groupId><artifactId>SpringBoot01</a…...
centos7如何安装wordpress/百度竞价广告代理
目录 题目描述:示例 :解法:题目描述: 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过根结点。 示例 : 给定二叉树 1/ \2 3/ \ 4 5 返回 3, 它的长度是…...
网站排名突然下降解决/百度代发收录
参考网址详细链接直达 任意浏览器的默认字体高都是16px 如果自己设置字体大小 按设置的字体大小转换 宽高也能使用 1em16px。那么12px0.75em,10px0.625em在css中的body选择器中声明Font-size62.5% px: 相对长度单位。像素px是相对于显示器屏幕分辨率而言的。 em:相对长度单…...
wordpress环境包/网络广告营销有哪些
关注公众号“AI算法修炼营”,选择“星标”公众号。精选作品,第一时间送达在图像几何变换时,无法给有些像素点直接赋值,例如,将图像放大两倍,必然会多出一些无法被直接映射的像素点,对于这些像素…...
网站怎么做sem/网站优化排名金苹果系统
建立一个JDBC应用程序,本教程中以Java连接MySQL为一个示例,分六个步骤进行: 1. 导入包 在程序中包含数据库编程所需的JDBC类。大多数情况下,使用 import java.sql.* 就足够了,如下所示: //STEP 1. Import r…...