微服务 Spring Cloud 7,Nacos配置中心的Pull原理,附源码
目录
- 一、本地配置
- 二、配置中心
- 1、以Nacos为例:
- 2、Pull模式
- 3、也可以通过Nacos实现注册中心
- 三、配置中心提供了哪些功能
- 四、如何操作配置中心
- 1、配置注册
- 2、配置反注册
- 3、配置查看
- 4、配置变更订阅
- 五、主流的微服务注册中心有哪些,如何选择?
- 1、在选择微服务注册中心时,可以考虑以下因素:
- 2、主流注册中心
- (1)Eureka
- (2)Consul
- (3)Zookeeper
- (4)Nacos
- 3、如何选择?
- 微服务 Spring Cloud系列
大家好,我是哪吒。
在单体服务时代,关于配置信息,管理一套配置文件即可。
而拆分成微服务之后,每一个系统都会有自己的配置,并且都各不相同,有些配置还需要动态改变,以达到动态降级、切流量、扩缩容等目的。
一、本地配置
在Spring Boot开发中,可以把配置项放到config文件中,把配置当代码使用。比如:
public class AppConfig {public static final String static_SUCCESS_CODE = "0000";public static final String static_ERROR_CODE = "0001";
}
也可以通过@Value加载yaml配置文件中的配置。
@Component
public class HttpConfig {// 核心线程数public static String config_CORE_POOL_SIZE;@Value("${async.corePoolSize}")public void setSaveUrl(String corePoolSize) {HttpConfig.config_CORE_POOL_SIZE = corePoolSize;}
}
无论是将配置定义在代码中,还是将配置写在yaml配置文件中,都相当于把配置存在应用程序的本地。
如果想修改配置,就需要将在Linux服务器中部署的程序停止,然后手动修改其配置,再进行重启。
如果修改的配置项较多,这也是一项容易出错,而且繁琐的事情,长期运维的小伙伴应该深有体会。
当时,我就在想,作为世界上使用人数最多的语言,更新一个配置,需要这么复杂吗?
答案肯定不是的。
二、配置中心
配置中心(Configuration Center)通常用于集中管理应用程序的配置信息。这些配置信息可以包括数据库连接信息、外部服务地址、日志级别、超时设置等。配置中心可以提高应用部署的灵活性和可维护性。
程序启动时,可以自动从配置中心拉取所需要的配置项,配置中心中配置有所改变时,同样可以自动从配置中心拉取最新的配置信息,服务不需要重新发布。
1、以Nacos为例:
- 配置中心的信息一般都是放在bootstrap.yml 中;
- 初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置;
- Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖;
- 然后再读取application.yml中的配置,进行配置合并,完成项目的启动。
项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。
2、Pull模式
Nacos采用的是Pull模式获取服务端数据,客户端采用长轮询的方式定时的发起Pull请求,去检查服务端配置信息是否发生了变化。
- 客户端发起长轮询请求,监听变更的dataId+group;
- 服务端收到客户端的请求,这时会挂起客户端的请求;
- 如果在服务端设计的29.5s之内都没有发生变更,触发自动检查机制,此时不管是否有变化,服务端都会返回响应到客户端
- 如果在29.5s之内配置项发生了变更,则会触发一个事件机制,将变更的数据推送的客户端。
3、也可以通过Nacos实现注册中心
这种是最简单的Nacos注册中心,有若干个服务,都注册到Nacos注册中心,调用之前,先到Nacos获取对应接口,然后进行实际的调用。
服务1和服务2和Nacos之间维护一个心跳关系,每5秒跳一次,频率不能太快或者太慢,否者会嗝屁的。
如果Nacos在5秒内没有收到心跳,则表示服务挂了,Nacos会下线此服务。
对于超过15秒没有收到客户端心跳的服务实例,会将它的healthy属性置为false,客户端无法调用healthy为false的服务。
如果超过30秒没有收到心跳,Nacos会直接将此服务剔除。
也可以通过服务端主动注销的方式,停止注册。
服务1调用服务2时,服务1会通过定时任务到Nacos中获取在线的服务,保证所调用的服务一直都是健康在线的状态。
获取到之后,用缓存将其保存起来,然后通过负载均衡器调用服务2,此时,将不再使用服务端的负载均衡Nginx了。
三、配置中心提供了哪些功能
- 配置项管理:支持添加、发布、修改配置项以及配置项的分组,可以实现版本管理,支持热发布、灰度发布、环境隔离,提供API接口与可视化操作页面。
- 权限控制:配置项访问控制,读权限和写权限。
- 操作审计:支持记录用户的操作行为。
- 配置变更:当有新的配置项或是现有的配置项发生变动时,配置中心能够进行实时的监控并做出相应的处理。
- 配置推送:通过订阅/发布的模式,将配置信息推送给各个服务的消费者。
- 历史版本管理:保存所有的配置历史版本,并提供查询和对比的功能,可以轻松的回滚到任何一个版本。
- 灰度发布:通过配置中心可以实现在部分环境中先发布新的配置项,观察一段时间没有问题后再推送给其他所有的环境。
- 配置变更审计:记录每次配置的修改记录,方便追踪和管理。
这些功能可以帮助降低分布式系统中管理配置信息的成本,降低因错误的配置信息变更带来可用性下降甚至发生故障的风险。
四、如何操作配置中心
1、配置注册
配置中心的配置注册通常包括以下步骤:
- 服务提供者在启动时,将自己的配置信息注册到配置中心。这些信息可能包括服务的IP地址、端口号、数据库连接信息等。
- 配置中心接收并存储这些配置信息。一般来说,配置中心会提供一个统一的接口或者界面,让服务提供者能够方便地进行注册。
- 服务消费者在需要使用某个服务时,会从配置中心获取相应的配置信息。这样,即使服务提供者的地址等信息发生变化,服务消费者也能通过配置中心获取到最新的配置信息,而无需手动修改。
通过这种方式,配置中心实现了配置的统一管理和动态更新。服务提供者和消费者都可以通过配置中心来进行配置的注册和获取,大大提高了配置的灵活性和便利性。同时,也减少了因为手动配置错误而导致的问题,提高了系统的稳定性和可用性。
要通过Java代码实现配置注册到配置中心,你可以遵循以下步骤。这里继续提供一个通用的示例代码,以展示基本的流程和步骤。请注意,实际的代码可能会因所使用的具体配置中心而有所不同。
import com.configcenter.sdk.ConfigCenterClient;
import com.configcenter.sdk.exception.ConfigCenterException;
import com.configcenter.sdk.model.Configuration; public class ConfigurationRegistration { public static void main(String[] args) { // 配置中心的服务器URL和认证令牌 String serverUrl = "configcenter_server_url"; String authToken = "your_auth_token"; // 创建配置对象 Configuration configuration = new Configuration(); configuration.setId("your_configuration_id"); configuration.setKey("your_configuration_key"); configuration.setValue("your_configuration_value"); // 可选:设置其他配置项属性,如描述、标签等 try { // 初始化配置中心的客户端 ConfigCenterClient client = ConfigCenterClient.init(serverUrl, authToken); // 调用配置中心的注册API boolean success = client.registerConfiguration(configuration); if (success) { System.out.println("配置注册成功"); } else { System.out.println("配置注册失败"); } } catch (ConfigCenterException e) { System.out.println("配置注册出现异常: " + e.getMessage()); e.printStackTrace(); } }
}
在这个示例中:
- 我们首先创建了一个Configuration对象,设置了配置项的ID、键(key)和值(value)。你可以根据需要设置其他属性,如描述、标签等;
- 然后,我们初始化配置中心的客户端,并调用client.registerConfiguration()方法来执行配置注册操作,传入创建好的Configuration对象。
- 该方法将返回一个布尔值,表示注册是否成功,我们根据返回结果打印相应的消息;
- 如果发生异常,我们进行异常处理并打印异常信息。
2、配置反注册
配置中心的配置反注册是指从配置中心中移除或注销某个配置项的过程。当某个服务或应用不再需要使用某个配置项时,可以进行配置反注册操作。这个操作通常通过配置中心提供的接口来完成,它会将指定的配置项从配置中心中删除或标记为注销状态。
配置反注册可以是手动触发的,也可以是自动触发的。在手动触发方式下,管理员或开发者可以通过调用配置中心提供的反注册接口,指定需要反注册的配置项。而自动触发方式下,通常会在服务或应用停止时,自动触发配置反注册操作。
配置反注册是配置中心的一个重要功能,它可以帮助管理员或开发者更好地管理配置项的生命周期,确保配置中心中的数据与实际应用需求保持一致。同时,通过反注册不再需要的配置项,也可以减少配置中心的存储空间和资源消耗。
要通过Java代码实现配置反注册,你需要使用配置中心提供的Java SDK或API。以下是一个示例代码,展示了如何使用Java来实现配置反注册。
import com.configcenter.sdk.ConfigCenterClient;
import com.configcenter.sdk.exception.ConfigCenterException; public class ConfigurationDeregistration { public static void main(String[] args) { // 配置中心的服务器URL和认证令牌 String serverUrl = "configcenter_server_url"; String authToken = "your_auth_token"; // 初始化配置中心的客户端 ConfigCenterClient client = ConfigCenterClient.init(serverUrl, authToken); // 配置项的ID String configurationId = "your_configuration_id"; try { // 调用配置中心的反注册API boolean success = client.deregisterConfiguration(configurationId); if (success) { System.out.println("配置反注册成功"); } else { System.out.println("配置反注册失败"); } } catch (ConfigCenterException e) { System.out.println("配置反注册出现异常: " + e.getMessage()); e.printStackTrace(); } }
}
在这个示例中:
- 我们首先通过ConfigCenterClient.init()初始化配置中心的客户端,传入服务器URL和认证令牌;
- 然后,我们调用client.deregisterConfiguration()方法来执行配置反注册操作,传入配置项的ID;
- 该方法将返回一个布尔值,指示反注册是否成功;
- 根据返回结果,我们打印相应的消息;
- 如果发生异常,我们捕获并打印异常信息。
3、配置查看
通过配置中心的用户界面和API接口,你可以方便地查看和管理配置项。
import com.configcenter.sdk.ConfigCenterClient;
import com.configcenter.sdk.exception.ConfigCenterException;
import com.configcenter.sdk.model.Configuration; public class ConfigurationViewer { public static void main(String[] args) { // 配置中心的服务器URL和认证令牌 String serverUrl = "configcenter_server_url"; String authToken = "your_auth_token"; // 配置项的ID或键(key) String configurationId = "your_configuration_id"; // 或者使用配置项的键(key)来获取配置项,根据配置中心API的要求而定 // String configurationKey = "your_configuration_key"; try { // 初始化配置中心的客户端 ConfigCenterClient client = ConfigCenterClient.init(serverUrl, authToken); // 调用配置中心的API接口获取配置项 Configuration configuration = client.getConfiguration(configurationId); // 或者使用配置项的键(key)来获取: // Configuration configuration = client.getConfigurationByKey(configurationKey); // 输出配置项的信息 if (configuration != null) { System.out.println("配置项ID: " + configuration.getId()); System.out.println("配置项键(key): " + configuration.getKey()); System.out.println("配置项值(value): " + configuration.getValue()); // 输出其他配置项属性,如描述、标签等 } else { System.out.println("未找到配置项"); } } catch (ConfigCenterException e) { System.out.println("查看配置项出现异常: " + e.getMessage()); e.printStackTrace(); } }
}
在上面的示例代码中:
- 我们首先初始化配置中心的客户端;
- 然后调用相应的API接口来获取配置项;
- 在获取到配置项后,我们可以输出配置项的各种属性,如ID、键(key)、值(value)等;
- 如果发生异常,我们进行异常处理并打印相应的异常信息。
4、配置变更订阅
配置中心通常支持配置变更订阅功能,允许应用程序或其他服务订阅配置项的变更通知。当配置项发生变化时,配置中心会向订阅者发送通知,以便订阅者能够及时获取最新的配置项。
要通过Java代码实现配置变更订阅,你可以按照以下步骤进行操作:
- 首先,确保你已经初始化了配置中心的客户端,如上述示例代码所示。
- 创建一个订阅者(listener)类,实现配置中心提供的订阅接口。这个类将负责处理配置项的变更通知。
- 在订阅者类中,实现接口中定义的方法,以处理配置项的变更事件。该方法通常会在配置项发生变化时被调用,并且会接收包含变更信息的参数。
- 在你的应用程序或服务中,创建一个订阅请求(subscription request)对象,指定你希望订阅的配置项或配置项的过滤条件。
- 使用配置中心的客户端对象,调用订阅方法,将订阅请求对象和订阅者对象作为参数传递给该方法。这将向配置中心注册你的订阅请求,并指定订阅者类来处理变更通知。
- 一旦注册成功,当配置项发生变更时,配置中心将调用订阅者类中的方法,将变更信息传递给订阅者。你可以在这个方法中编写逻辑来处理配置项的变更,如更新本地缓存、重新加载配置等。
下面是一个简单的示例代码,展示如何实现配置变更订阅:
import com.configcenter.sdk.ConfigCenterClient;
import com.configcenter.sdk.exception.ConfigCenterException;
import com.configcenter.sdk.listener.ConfigurationChangeListener;
import com.configcenter.sdk.model.Configuration; public class ConfigurationSubscriber { public static void main(String[] args) { // 初始化配置中心的客户端(省略代码) // 创建订阅者类 ConfigurationChangeListener listener = new ConfigurationChangeListener() { @Override public void onConfigurationChanged(Configuration configuration) { // 处理配置项变更事件 System.out.println("配置项发生变化: " + configuration.getKey() + " = " + configuration.getValue()); // 在这里可以更新本地缓存、重新加载配置等 } }; try { // 创建订阅请求对象(根据配置中心API的要求而定) // 指定你希望订阅的配置项过滤条件,如配置项的键(key)或其他属性 // 调用配置中心的订阅方法 ConfigCenterClient client = ConfigCenterClient.init(serverUrl, authToken); client.subscribeToConfigurationChanges(filter, listener); } catch (ConfigCenterException e) { System.out.println("配置变更订阅出现异常: " + e.getMessage()); e.printStackTrace(); } }
}
五、主流的微服务注册中心有哪些,如何选择?
1、在选择微服务注册中心时,可以考虑以下因素:
- 功能和特性:不同的注册中心可能具有不同的功能和特性,需要根据自己的需求来选择。例如,一些注册中心可能更侧重于服务发现,而另一些可能提供更全面的配置管理功能。
- 性能和稳定性:注册中心作为微服务架构的核心组件,其性能和稳定性至关重要。需要对候选的注册中心进行性能测试和稳定性评估,确保它们能够满足你的业务需求。
- 易用性和开发体验:注册中心的易用性和开发体验也是选择的重要因素。选择一个提供良好开发文档、客户端库和工具的注册中心,可以使开发过程更加顺畅高效。
- 社区支持和生态系统:一个活跃和健康的社区可以为注册中心提供持续的支持和改进。同时,一个丰富的生态系统可以提供更多的集成选项和解决方案。因此,需要评估候选注册中心的社区活跃度和生态系统成熟度。
- 安全性和合规性:安全性和合规性对于任何系统都至关重要。需要确保所选的注册中心能够提供足够的安全保障,并满足你的合规性要求。
最终,选择微服务注册中心是一个权衡的过程,需要根据自己的实际需求、技术栈、团队熟悉度等因素进行综合考虑。
2、主流注册中心
在Eureka、Consul、Zookeeper和Nacos这几个微服务注册中心中,选择最适合的一个取决于你的具体需求和环境。
(1)Eureka
- 是Netflix开发的服务注册中心,与Spring Cloud集成良好。
- 保证了高可用性和最终一致性,服务注册相对较快。
- 在数据不一致时,每个Eureka节点仍能正常对外提供服务,保证了可用性。
(2)Consul
- 使用Go语言编写,具有较好的跨平台性。
- 提供了丰富的功能和强大的一致性保证。
- 支持多数据中心和分布式部署,适合大规模环境。
(3)Zookeeper
- 最初是一个分布式协调服务,后来被用于服务注册和发现。
- 提供了强一致性和高可用性,但相对较复杂,需要自行实现一些服务发现功能。
(4)Nacos
- 是阿里巴巴开源的项目,支持基于DNS和基于RPC的服务发现。
- 除了服务注册和发现,还支持动态配置服务。
- 在Spring Cloud中使用较为简单,只需简单的配置即可完成服务的注册和发现。
3、如何选择?
- 如果你使用Spring Cloud作为微服务框架,Eureka可能是一个自然的选择,因为它与Spring Cloud集成良好。
- 如果你需要强大的跨平台支持和一致性保证,Consul是一个不错的选择。
- 如果你已经在使用Zookeeper或其他Apache项目,并且希望在同一生态系统中解决服务注册和发现问题,那么Zookeeper可能适合你。
- 如果你在寻找一个简单且与阿里巴巴技术栈集成的解决方案,Nacos是一个值得考虑的选项。
最终的选择应基于你的技术需求、团队熟悉度和业务场景。
微服务 Spring Cloud系列
微服务 Spring Cloud 1,服务如何拆分?使用微服务的注意事项?
微服务 Spring Cloud 2,一文讲透微服务核心架构(注册中心、服务通信、服务监控、服务追踪、服务治理)
微服务 Spring Cloud 3,如何对微服务进行有效的监控?
微服务 Spring Cloud 4,分布式系统如何进行数据分区
微服务 Spring Cloud 5,一图说透Spring Cloud微服务架构
微服务 Spring Cloud 6,用了这么多年Docker容器,殊不知你还有这么多弯弯绕
🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师。
华为OD机试 2023B卷题库疯狂收录中,刷题点这里
刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。
相关文章:

微服务 Spring Cloud 7,Nacos配置中心的Pull原理,附源码
目录 一、本地配置二、配置中心1、以Nacos为例:2、Pull模式3、也可以通过Nacos实现注册中心 三、配置中心提供了哪些功能四、如何操作配置中心1、配置注册2、配置反注册3、配置查看4、配置变更订阅 五、主流的微服务注册中心有哪些,如何选择?…...
c#Nettonsoft.net库常用的方法json序列化反序列化
Newtonsoft.Json 是一个流行的 JSON 操作库,用于在 .NET 应用程序中序列化、反序列化和操作 JSON 数据。下面是 Newtonsoft.Json 常用的一些方法: 序列化对象为 JSON 字符串: string json JsonConvert.SerializeObject(obj);var obj new {…...

力扣刷题-二叉树-二叉树的高度与深度
二叉树最大深度 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 递归法 本题可以使用前序(中左…...
Vue3新增加的css语法糖
一、deep <template><div class""><el-input /> </div> </template> <style scoped> /* 样式穿透 */ :deep input {background: red; } </style> 二、slotted 子组件修改插槽里面的样式 <template><div clas…...

Windows安装Vmware 虚拟机
目录 一、Vmware 虚拟机介绍 二、Vmware 虚拟机的三种网络模式 2.1桥接模式 2.2仅主机模式 2.3NAT 网络地址转换模式 三、Vmware 虚拟机的安装 一、Vmware 虚拟机介绍 VMware Workstation Pro 是一款可以在个人电脑的操作系统上创建一个完全与主机操作系统隔离的 虚拟机&…...
uniapp地图手动控制地图scale
前言 首次使用uniapp开发地图过程中,发现uniapp地图居然没有提供手动控制地图scale的方法,这个也着实没有想到,查了半天资料,也终于找到一个方法能够比较好的控制scale,做个记录。 代码 要定义一个地图mapÿ…...

Kotlin学习之函数
原文链接 Understanding Kotlin Functions 函数对于编程语言来说是极其重要的一个组成部分,函数可以视为是程序的执行,是真正活的代码,为啥呢?因为运行的时候你必须要执行一个函数,一般从主函数入口,开始一…...

若依启动步骤
1.创建数据库 2.启动redis 3.改后端的数据库连接配置 4.配置redis redis的地址:cmd中ipconfig命令查看 6.启动后端:如下 7.启动前端ruoyi-ui中 先运行npm install,再npm run dev。项目就启动成功了。 用户名:admin 密码&#x…...

qt-C++笔记之两个窗口ui的交互
qt-C笔记之两个窗口ui的交互 code review! 文章目录 qt-C笔记之两个窗口ui的交互0.运行1.文件结构2.先创建widget项目,搞一个窗口ui出来3.项目添加第二个widget窗口出来4.补充代码4.1.qt_widget_interaction.pro4.2.main.cpp4.3.widget.h4.4.widget.cpp4.5.second…...

Redis-核心数据结构
五种数据结构 String结构 String结构应用场景 Hash结构 Hash结构应用场景 List结构 List结构应用场景 Set结构 Set结构应用场景 ZSet有序结构 ZSet有序结构应用场景...

设计模式—结构型模式之外观模式(门面模式)
设计模式—结构型模式之外观模式(门面模式) 外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。 例子 我们的电脑会有很多 组件&am…...
CentOS Stream 9-使用 systemd 管理自己程序时自定义日志路径
systemd 文件 [rootnode1 ~]# cat /etc/systemd/system/spms-wvp.service [Unit] DescriptionWVP service [Service] # 关键配置部分,注意这里的 spms-wvp ,后面需要用 SyslogIdentifierspms-wvp StandardOutputsyslog StandardErrorsyslog Typesimple Environment…...

动态页面调研及设计方案
文章目录 vue2 动态表单、动态页面调研一、form-generator二、ng-form-element三、Variant Form四、form-create vue2 动态表单、动态页面调研 一、form-generator 预览:https://mrhj.gitee.io/form-generator/#/ Vue2 Element UI支持拖拽生成表单不支持其他组件…...

鸿蒙4.0开发笔记之DevEco Studio之配置代码片段快速生成(三)
一、作用 配置代码片段可以让我们在Deveco Studio中进行开发时快速调取常用的代码块、字符串或者某段具有特殊含义的文字。其实现方式类似于调用定义好变量,然而这个变量是存在于Deveco Studio中的,并不会占用项目的资源。 二、配置代码段的方法 1、打…...

HarmonyOS真机调试报错:INSTALL_PARSE_FAILED_USESDK_ERROR处理
文章目录 1、 新建应用时选择与自己真机匹配的sdk版本2、 根据报错提示连接打开处理方案3、查询真机版本对应的**compileSdkVersion** 和 **compatibleSdkVersion** 提示3.1版本之后和3.1版本之前的不同命令(此处为3.0版本)4、根据查询修改参数5、连接成…...
webSocket基于面向对象二次封装
今天不睡,熬夜赶了个WebSocket 二次封装,也对这几天文章摸鱼感到抱歉,所以我出了一个注释非常非常全的代码 思路如下 首先,需要通过调用connect方法来建立WebSocket连接。当连接成功时,会调用我提供的回调函数,并将连接成功的消息帧作为参数…...

【Web】PHP反序列化的一些trick
目录 ①__wakeup绕过 ②加号绕过正则匹配 ③引用绕过相等 ④16进制绕过关键词过滤 ⑤Exception绕过 ⑥字符串逃逸 要中期考试乐(悲) ①__wakeup绕过 反序列化字符串中表示属性数量的值 大于 大括号内实际属性的数量时,wakeup方法会被绕过 (php5-p…...

【测试功能篇 01】Jmeter 压测接口最大并发量、吞吐量、TPS
压力测试,我们针对比较关键的接口,可以进行相应的压力测试,主要还是测试看看接口能抗住多少的请求数,TPS稳定在多少,也就是吞吐量多少 安装 Jmeter的安装很简单,官网下载地址 http://jmeter.apache.org/ &…...

代码随想录算法训练营第四十九天| 123.买卖股票的最佳时机III 188.买卖股票的最佳时机IV
文档讲解:代码随想录 视频讲解:代码随想录B站账号 状态:看了视频题解和文章解析后做出来了 123.买卖股票的最佳时机III class Solution:def maxProfit(self, prices: List[int]) -> int:if len(prices) 0:return 0dp [[0] * 5 for _ in…...

11.20 知识总结(choices参数、MVC和MTV的模式、Django与Ajax技术)
一、 choices参数的使用 1.1 作用 针对某个可以列举完全的可能性字段,我们应该如何存储 .只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数 1.2 应用场景 应用场景: 学历: 小学 初中 高中 本科 硕士…...

Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...

Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...