源码解析-Spring Eureka(更新ing)
源码解析-Spring Eureka
文章目录
- 源码解析-Spring Eureka
- 前言
- 一、从Spring.factory和注解开始
- 二、重要的一步EurekaServerInitializerConfiguration
- 三、初始化了什么?
- 自动保护
- 四, 重新回到EurekaServerAutoConfiguration
前言
无
一、从Spring.factory和注解开始
我们可以看到,Eureka通过spring boot的自动配置机制引入了一个类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
通过这个配置我们找到对应的配置类,可以看到,这个配置类使用了Marker作为条件注入
@Configuration(proxyBeanMethods = false
)
@Import({EurekaServerInitializerConfiguration.class})
@ConditionalOnBean({EurekaServerMarkerConfiguration.Marker.class})
@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration implements WebMvcConfigurer
这个时候我们返回查看我们配置一个eureka所需要的基本注解可以看到,我们正在这个这个@EnableEurekaServer 注解里面初始化了这个类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.cloud.netflix.eureka.server;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}
通过spring.factory的自动配置以及@EnableEurekaServer 就可以实现eureka服务端的手动注入(通过加入注解)
二、重要的一步EurekaServerInitializerConfiguration
在上面的EurekaServerAutoConfiguration里面我们可以看到它import了一个初始化类
注意在这个初始化类实现了SmartLifeCycle接口,实现了其Start方法
@Configuration(proxyBeanMethods = false
)
public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered
实现的start方法,会在bean在启动的时候调用,该方法会new一个线程并发布订阅
public void start() {(new Thread(() -> {try {this.eurekaServerBootstrap.contextInitialized(this.servletContext);log.info("Started Eureka Server");this.publish(new EurekaRegistryAvailableEvent(this.getEurekaServerConfig()));this.running = true;this.publish(new EurekaServerStartedEvent(this.getEurekaServerConfig()));} catch (Exception var2) {log.error("Could not initialize Eureka servlet context", var2);}})).start();}
可以看到,通过这个start方法,eureka初始化了它自己的context上下文并发布了一些事件。
三、初始化了什么?
进入到contextInitialized方法,我们可以看到
public void contextInitialized(ServletContext context) {try {this.initEurekaEnvironment();this.initEurekaServerContext();context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);} catch (Throwable var3) {log.error("Cannot bootstrap eureka server :", var3);throw new RuntimeException("Cannot bootstrap eureka server :", var3);}}
eureka首先初始化了配置信息,然后进行上下文的初始化
protected void initEurekaServerContext() throws Exception {JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);if (this.isAws(this.applicationInfoManager.getInfo())) {this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig, this.eurekaClientConfig, this.registry, this.applicationInfoManager);this.awsBinder.start();}EurekaServerContextHolder.initialize(this.serverContext);log.info("Initialized server context");int registryCount = this.registry.syncUp();this.registry.openForTraffic(this.applicationInfoManager, registryCount);EurekaMonitors.registerAllStats();}
进入到initEurekaServerContext方法,我们可以看到几个重要的方法
在openForTraffic方法里面
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {this.expectedNumberOfClientsSendingRenews = count;this.updateRenewsPerMinThreshold();logger.info("Got {} instances from neighboring DS node", count);logger.info("Renew threshold is: {}", this.numberOfRenewsPerMinThreshold);this.startupTime = System.currentTimeMillis();if (count > 0) {this.peerInstancesTransferEmptyOnStartup = false;}DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();boolean isAws = Name.Amazon == selfName;if (isAws && this.serverConfig.shouldPrimeAwsReplicaConnections()) {logger.info("Priming AWS connections for all replicas..");this.primeAwsReplicas(applicationInfoManager);}logger.info("Changing status to UP");applicationInfoManager.setInstanceStatus(InstanceStatus.UP);super.postInit();}
我们重点关注这里的super.postInit()
protected void postInit() {this.renewsLastMin.start();if (this.evictionTaskRef.get() != null) {((EvictionTask)this.evictionTaskRef.get()).cancel();}this.evictionTaskRef.set(new EvictionTask());this.evictionTimer.schedule((TimerTask)this.evictionTaskRef.get(), this.serverConfig.getEvictionIntervalTimerInMs(), this.serverConfig.getEvictionIntervalTimerInMs());}
可以看到this.evictionTaskRef.set(new EvictionTask());,这里注册了一个剔除任务
int registrySize = (int)this.getLocalRegistrySize();int registrySizeThreshold = (int)((double)registrySize * this.serverConfig.getRenewalPercentThreshold());int evictionLimit = registrySize - registrySizeThreshold;int toEvict = Math.min(expiredLeases.size(), evictionLimit);
这里的剔除与eureka配置里面的自我保护配置有关
自动保护
在eureka中,如果打开了自我保护配置并设置了剔除阈值,eureka集群就会在计算正常超过阈值的时候执行上面的代码把的节点给剔除
- 如果现在有10个节点,7个节点是正常,3个节点是由有问题的,阈值设置了80%,这个时候7个节点中的一个节点出现了问题,但是没有超过阈值(变成了60%),这个时候就会访问到失败的节点
- 如果现在有100个节点,3个节点有问题,阈值也是80%,现在的值是(97%)超过了阈值,如果这个时候有节点出现问题则会立即剔除,
- 但是不能把自我保护关闭,如果3个节点是因为波动导致的暂时访问不到则会立即被剔除
eureka:server:enable-self-preservation: true
eureka:server:renewal-percent-threshold: 0.85
我们再进到syncUp方法里面
public int syncUp() {int count = 0;for(int i = 0; i < this.serverConfig.getRegistrySyncRetries() && count == 0; ++i) {if (i > 0) {try {Thread.sleep(this.serverConfig.getRegistrySyncRetryWaitMs());} catch (InterruptedException var10) {logger.warn("Interrupted during registry transfer..");break;}}Applications apps = this.eurekaClient.getApplications();Iterator var4 = apps.getRegisteredApplications().iterator();
可以看到当一个eureka服务启动的时候,会作为一个eureka客户端去peer节点拉取配置(这也是eureka为什么不是强一致性的)
四, 重新回到EurekaServerAutoConfiguration
首先就是eureka的控制器类,eureka的dashboard上面的数据通过这个控制器(springWeb)来获取
@Bean@ConditionalOnProperty(prefix = "eureka.dashboard",name = {"enabled"},matchIfMissing = true)public EurekaController eurekaController() {return new EurekaController(this.applicationInfoManager);}
接着再eurekaServerContext里面实例化了属于eureka的上下文
@Bean@ConditionalOnMissingBeanpublic EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager);}
进入context的初始化方法,可以看在context初始化里面有重要的一环就是设置三级缓存initializedResponseCache
public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {this.numberOfReplicationsLastMin.start();this.peerEurekaNodes = peerEurekaNodes;this.initializedResponseCache();this.scheduleRenewalThresholdUpdateTask();this.initRemoteRegionRegistry();try {Monitors.registerObject(this);} catch (Throwable var3) {logger.warn("Cannot register the JMX monitor for the InstanceRegistry :", var3);}}
里面有一个定时任务,就是定期刷新缓存
if (this.shouldUseReadOnlyResponseCache) {this.timer.schedule(this.getCacheUpdateTask(), new Date(System.currentTimeMillis() / responseCacheUpdateIntervalMs * responseCacheUpdateIntervalMs + responseCacheUpdateIntervalMs), responseCacheUpdateIntervalMs);}
继续查看自动配置类,可以看到,eureka通过jersey框架包装了注册服务
@Beanpublic FilterRegistrationBean<?> jerseyFilterRegistration(Application eurekaJerseyApp) {FilterRegistrationBean<Filter> bean = new FilterRegistrationBean();bean.setFilter(new ServletContainer(eurekaJerseyApp));bean.setOrder(Integer.MAX_VALUE);bean.setUrlPatterns(Collections.singletonList("/eureka/*"));return bean;}
再看jerseyApplication,这里面会对eureka的包路径进行扫描, 并将其中的候选类进行注入,其中非常重要的就是resource目录下的applicationsresoure,该方法会返回一个Application的Bean(SPring的config加Bean)
@Beanpublic Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) {ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment);provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));Set<Class<?>> classes = new HashSet();String[] var5 = EUREKA_PACKAGES;int var6 = var5.length;for(int var7 = 0; var7 < var6; ++var7) {String basePackage = var5[var7];Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);Iterator var10 = beans.iterator();while(var10.hasNext()) {BeanDefinition bd = (BeanDefinition)var10.next();Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader());classes.add(cls);}}Map<String, Object> propsAndFeatures = new HashMap();propsAndFeatures.put("com.sun.jersey.config.property.WebPageContentRegex", "/eureka/(fonts|images|css|js)/.*");DefaultResourceConfig rc = new DefaultResourceConfig(classes);rc.setPropertiesAndFeatures(propsAndFeatures);return rc;}
在applicationsResource里面,通过jersey编写一系列关于**注册中心的“注册”,“取消”,“续约”**等的HTTP方法
我们先来看获取ALLKey的方法(拉取所有配置)
@GETpublic Response getContainers(@PathParam("version") String version, @HeaderParam("Accept") String acceptHeader, @HeaderParam("Accept-Encoding") String acceptEncoding, @HeaderParam("X-Eureka-Accept") String eurekaAccept, @Context UriInfo uriInfo, @Nullable @QueryParam("regions") String regionsStr) {boolean isRemoteRegionRequested = null != regionsStr && !regionsStr.isEmpty();String[] regions = null;if (!isRemoteRegionRequested) {EurekaMonitors.GET_ALL.increment();} else {regions = regionsStr.toLowerCase().split(",");Arrays.sort(regions);EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment();}if (!this.registry.shouldAllowAccess(isRemoteRegionRequested)) {return Response.status(Status.FORBIDDEN).build();} else {CurrentRequestVersion.set(Version.toEnum(version));Key.KeyType keyType = KeyType.JSON;String returnMediaType = "application/json";if (acceptHeader == null || !acceptHeader.contains("json")) {keyType = KeyType.XML;returnMediaType = "application/xml";}Key cacheKey = new Key(EntityType.Application, "ALL_APPS", keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions);Response response;if (acceptEncoding != null && acceptEncoding.contains("gzip")) {response = Response.ok(this.responseCache.getGZIP(cacheKey)).header("Content-Encoding", "gzip").header("Content-Type", returnMediaType).build();} else {response = Response.ok(this.responseCache.get(cacheKey)).build();}CurrentRequestVersion.remove();return response;}}
其中ResponseCache注入了了eureka自己实现的三级缓存的getValue方法
@VisibleForTestingValue getValue(Key key, boolean useReadOnlyCache) {Value payload = null;try {if (useReadOnlyCache) {Value currentPayload = (Value)this.readOnlyCacheMap.get(key);if (currentPayload != null) {payload = currentPayload;} else {payload = (Value)this.readWriteCacheMap.get(key);this.readOnlyCacheMap.put(key, payload);}} else {payload = (Value)this.readWriteCacheMap.get(key);}} catch (Throwable var5) {logger.error("Cannot get value for key : {}", key, var5);}return payload;}
我们再来看在eureka中一个服务是如何被维护的
在applicationResoure中,有添加服务的方法
@POST@Consumes({"application/json", "application/xml"})public Response addInstance(InstanceInfo info, @HeaderParam("x-netflix-discovery-replication") String isReplication) {logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);if (this.isBlank(info.getId())) {return Response.status(400).entity("Missing instanceId").build();} else if (this.isBlank(info.getHostName())) {return Response.status(400).entity("Missing hostname").build();} else if (this.isBlank(info.getIPAddr())) {return Response.status(400).entity("Missing ip address").build();} else if (this.isBlank(info.getAppName())) {return Response.status(400).entity("Missing appName").build();} else if (!this.appName.equals(info.getAppName())) {return Response.status(400).entity("Mismatched appName, expecting " + this.appName + " but was " + info.getAppName()).build();} else if (info.getDataCenterInfo() == null) {return Response.status(400).entity("Missing dataCenterInfo").build();} else if (info.getDataCenterInfo().getName() == null) {return Response.status(400).entity("Missing dataCenterInfo Name").build();} else {DataCenterInfo dataCenterInfo = info.getDataCenterInfo();if (dataCenterInfo instanceof UniqueIdentifier) {String dataCenterInfoId = ((UniqueIdentifier)dataCenterInfo).getId();if (this.isBlank(dataCenterInfoId)) {boolean experimental = "true".equalsIgnoreCase(this.serverConfig.getExperimental("registration.validation.dataCenterInfoId"));if (experimental) {String entity = "DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id";return Response.status(400).entity(entity).build();}if (dataCenterInfo instanceof AmazonInfo) {AmazonInfo amazonInfo = (AmazonInfo)dataCenterInfo;String effectiveId = amazonInfo.get(MetaDataKey.instanceId);if (effectiveId == null) {amazonInfo.getMetadata().put(MetaDataKey.instanceId.getName(), info.getId());}} else {logger.warn("Registering DataCenterInfo of type {} without an appropriate id", dataCenterInfo.getClass());}}}this.registry.register(info, "true".equals(isReplication));return Response.status(204).build();}}
进去到register方法里面,我们可以看到,eureka首先向自己注册了当前服务,然后同步到了peer节点上面
public void register(InstanceInfo info, boolean isReplication) {int leaseDuration = 90;if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {leaseDuration = info.getLeaseInfo().getDurationInSecs();}super.register(info, leaseDuration, isReplication);this.replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, (InstanceInfo.InstanceStatus)null, isReplication);}
可以看到,register里面,我们的InstanceInfo 被一个Map<String, Lease> gMap维护着
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {this.read.lock();try {Map<String, Lease<InstanceInfo>> gMap = (Map)this.registry.get(registrant.getAppName());EurekaMonitors.REGISTER.increment(isReplication);if (gMap == null) {ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap();gMap = (Map)this.registry.putIfAbsent(registrant.getAppName(), gNewMap);if (gMap == null) {gMap = gNewMap;}}
我们看下Lease的结构,通过下面的这种结构,我们只需要修改Lease的long信息就可以对当前节点的生命状态进行修改而不需要修改节点本身
public class Lease<T> {public static final int DEFAULT_DURATION_IN_SECS = 90;private T holder; // 具体的实例信息// 一些用于维护节点状态的时间信息private long evictionTimestamp;private long registrationTimestamp;private long serviceUpTimestamp;private volatile long lastUpdateTimestamp;private long duration;相关文章:
源码解析-Spring Eureka(更新ing)
源码解析-Spring Eureka 文章目录 源码解析-Spring Eureka前言一、从Spring.factory和注解开始二、重要的一步EurekaServerInitializerConfiguration三、初始化了什么?自动保护 四, 重新回到EurekaServerAutoConfiguration 前言 无 一、从Spring.factory和注解开始…...
python调用百度通用翻译API
文章目录 1. 简介2. 使用步骤3. api调用实现4. 编码实现 1. 简介 前段时间在做视频语音识别生成多语种字幕时,使用了百度翻译通用翻译api进行翻译。百度翻译平台经过个人认证之后,每月有200万字符的免费翻译额度。还是比较舒服的。 百度翻译开放平台是百…...
Timeline动画「硬切」的问题
1)Timeline动画「硬切」的问题 2)移动平台纹理压缩格式选择ASTC,美术出图还需遵守POT吗 3)如何去掉DOTS Unity.Entities.Graphics创建的BatchRendererGroup的UI相机回调 4)Timeline播放动画会产生位移的问题 这是第409…...
CentOS 9 配置网卡
在 CentOS 9 中配置网卡,通常涉及以下几个步骤: 1. 查看网络接口 首先,确认系统上存在的网络接口。可以使用 ip 命令或 ifconfig 命令查看网络接口的状态。 ip a 或者: ifconfig 这将列出所有可用的网络接口(例如…...
redis7.x源码分析:(2) adlist双向链表
链表是一种常用的数据结构(如果不了解,请先学习数据结构),由于c语言本身没有实现标准的链表库,所以redis自己实现了一个双向链表。 双向链表在redis内部的使用非常的多,几乎所有模块中都有用到。 下面看下它…...
KUKU FM 音频Linux平台免费下载工具
1.工具名称:kuku-dl 功能: ✅ 下载播客、故事和有声读物! ✅ 获取所有元数据和封面艺术品。 ✅ 支持字幕! 3.使用说明: 3.1. 直接镜像github源码库 👉 git clone https://github.com/bunnykek/kuku-…...
《Django 5 By Example》阅读笔记:p105-p164
《Django 5 By Example》学习第5天,p105-p164总结,总计60页。 一、技术总结 1.文章标签功能 Django自带django-taggit。 2.自定义template tags 3.roadmap功能 4.RSS功能 5.full-text搜索功能 这里使用的是Postgresql,使用pip install psycopg安…...
网络延迟对Python爬虫速度的影响分析
Python爬虫因其强大的数据处理能力和灵活性而被广泛应用于数据抓取和网络信息收集。然而,网络延迟是影响爬虫效率的重要因素之一。本文将深入探讨网络延迟对Python爬虫速度的影响,并提供相应的代码实现过程,以帮助开发者优化爬虫性能。 网络…...
微信小程序内嵌h5页面(uniapp写的),使用uni.openLocation无法打开页面问题
1.问题 微信小程序内嵌h5页面(uniapp写的),使用uni.openLocation打开地图页面后,点击该页面下方“到这里”按钮,显示无法打开。如下图: 3.解决方案 在内嵌h5中不使用uniapp的api打开地图,而在h5页面事件处理程序中去跳转新的小程序页面,在该新页面去使用微信小程序…...
创建一个简单的基于STM32的FreeRTOS应用
使用STM32CubeIDE生成。 1,使能FreeRTOS 2,选择版本 CMSIS_V1 3 设置参数USE_NEWLIB_REENTRANT 如果不设置,会在生成代码的时候提示错误 4,设置时钟TIM1作为系统时钟 5,设置Task …...
【Revit二次开发】创建Ribbon选项卡与带图标的按钮
效果图 创建一个叫做“开发的插件”的选项卡, 选项卡内有一个叫做“Hello”的图标按钮, 点击按钮后运行一个命令, 该命令弹出提示框“Hello Revit!”。 在此示例基础上,可以根据需要替换图标、文字、命令功能。 步骤 安装Revit…...
Win11 终端执行 python xxx.py 没反应
在 Win11 上写了一段 Python 代码来分析日志文件, 发现执行没反应。是在 VSCode 里的终端中执行的 python log_stats.py, 是 PowerShell; 也尝试了 cmd, 情况一样。 一开始怀疑代码写错,直到故意在代码里加打印,发现没…...
使用视频提升应用在 App Store 中的推广效果
App Store 上有485 万个应用和游戏。每个应用开发者都知道,要在如此庞大的市场中脱颖而出,吸引宝贵的用户眼球,是多么困难。 您需要在应用推广游戏中尝试一些不同的东西,那就是视频预览。这些短小的电影奇迹已经成为应用营销人员…...
对话 OpenCV 之父 Gary Bradski:灾难性遗忘和持续学习是尚未解决的两大挑战 | Open AGI Forum
作者 | Annie Xu 采访、责编 | Eric Wang 出品丨GOSIM 开源创新汇 Gary Bradski,旺盛的好奇心、敢于冒险的勇气、独到的商业视角让他成为计算视觉、自动驾驶领域举重若轻的奠基者。 Gary 曾加入 Stanley 的团队,帮助其赢得 2005 年美国穿越沙漠 DA…...
通过地址获取LONG和LAT并且存入csv
通过地址获取LONG和LAT并且存入csv 1. Address存在Address这个column里,从网上复制(如果可以爬虫自动更好) 2. 用代码获取GPS,再存入表格 import pandas as pd from geopy.geocoders import Nominatim from time import sleep#…...
Nginx SSL+tomcat,使用request.getScheme() 取到https协议
架构上使用了 Nginx tomcat 集群, 且nginx下配置了SSL,tomcat no SSL,项目使用https和http协议。 发现 request.getScheme() //总是 http,而不是实际的http或https request.isSecure() //总是false(因为总是http) request.getRemoteAddr(…...
Node.Js+Knex+MySQL增删改查的简单示例(Typescript)
数据库: CREATE DATABASE MyDB; CREATE TABLE t_users (user_id int(11) NOT NULL,user_name varchar(10) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8; 项目结构: package.json如下,拷贝并替换你们本地的package.json后运行 npm install 命令安装所需要的依赖。…...
机器学习的概览
笔记内容侵权联系删除 机器学习算法 机器学习(包括深度学习分支)是研究“学习算法”的一门学问。所谓“学习”是指:对于某类任务T和性能度量P,一个计算机程序在T上以P衡量的性能随着经验E而自我完善,那么我们称这个计算机程序在从经验E学习。 机器学习…...
方法论-WPS模型(高效沟通和决策分析的框架)
WPS模型(What, Problem, Solution)是一种高效沟通和决策分析的框架,旨在帮助沟通者清晰、简洁地表达问题和解决方案,特别适用于在复杂或多变的环境中进行清晰的交流。WPS模型的核心是通过以下三个步骤来组织沟通内容: …...
OpenTelemetry 赋能DevOps流程的可观测性革命
作者:天颇 引言 在当今快节奏的软件开发和运维环境中,DevOps 已经成为主流,它通过整合开发和运维流程,推动着软件的快速迭代和持续交付。然而,随着微服务、容器化和云计算等技术的普及,系统复杂性急剧增加…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
