【Nacos无压力源码领读】(三) Nacos 配置中心与热更新原理详解 敢说全网最细

news2024/12/26 11:58:55

本文将从 Nacos 配置中心的基本使用入手, 详细介绍 Nacos 客户端发布配置, 拉取配置, 订阅配置的过程以及服务器对应的处理过程;

配置订阅以及热更新原理相关的部分, 我看了主流的博客网站, 绝对没有比这更详细的讲解;

如果在阅读过程中对文中提到的 SpringBoot 启动过程以及扩展机制不太了解, 或者出现了没见过的词, 在这篇文章 SpringBoot启动流程与配置类处理机制详解, 附源码与思维导图 中都能找到答案, 强烈建议学习后再来读本文;

对SpringBoot 属性加载机制不了解的可以看这里 SpringBoot 属性加载机制

Nacos配置中心

基本使用

一个大型项目, 服务数量会非常多, 每个服务都需要进行配置, 这会导致: 公共的一些配置需要修改时, 需要修改大量服务的配置文件; 并且需要重新启动涉及的所有服务;

Nacos就提供了作为配置中心的功能; 将配置(不仅是公共的, 服务独有的配置也可以保存到Nacos)保存在Nacos服务器中, 实现以中心化 (都在Nacos里)、外部化 (配置独立于服务之外保存) 和动态化 (动态化即可以实时刷新配置) 的方式管理配置。

基本原理

将配置以键值对的形式保存到Nacos中;

服务启动时, 从 Nacos 读取配置, 生成 PropertySource 对象, 保存到 Spring 容器中, 可以使用 @Value注解 等方式进行注入;

配置模型

为了更好地管理配置, 使得一个Nacos配置中心能支持多个应用, 多个服务, Nacos划分了几个不同的维度来组织保存的配置:

命名空间 Namespace

  • 用于用户粒度的配置隔离, 每一个命名空间都有一个唯一的ID与之对应, 默认情况下使用 public 命名空间;
  • 创建一个命名空间时, Nacos 会自动分配一个唯一的ID, 不需要用户指定;

分组 Group

  • 每个分组都有一个唯一的组名, 默认使用DEFAULT-GROUP分组;
  • 创建分组时, 由用户指定分组名称;

配置集 Data ID

  • 配置集是一组配置项的集合, 类比一个服务的一个配置文件;
  • 每个配置集都有一个唯一的 Data ID, 需要用户在创建配置集时手动指定;
  • SpringBoot项目启动时, 会自动生成一个配置集的DataID, 并从Nacos下载对应的配置;
  • 生成的DataID由三部分构成
${prefix}-${spring.profiles.active}.${file-extension}
  • 第一个字段 默认取spring.application.name的值, 也可以通过配置项 spring.cloud.nacos.config.prefix 来配置。

  • 第二个字段, 取spring.profiles.active的值, 并且在最前面拼接一个短横杠, 如果该属性没有配置, 这个字段为空, 也没有短横杠;

    spring.profiles.active用于指定激活的 Spring 配置文件。application.yml文件始终有效, 可以选择是否激活其它的配置文件; 可以根据不同的环境(例如开发、测试、生产等)来加载不同的配置,从而实现环境隔离和配置管理。

  • 第三个字段通过spring.cloud.nacos.config.file-extension属性配置,可以是 propertiesyaml;

  • 例如一个名称为 student-service 的服务, 激活了dev 配置文件, 配置的后缀是 yaml, 那么生成的DataID是

    student-service-dev.yaml;

配置项 Key - Value

  • 一个配置项就对应一个配置, 将配置的参数名与取值以键值对的形式保存;

使用Nacos配置中心

导包

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

在单个服务中, 添加配置文件bootstrap.yml, 将和 Nacos-Config 相关的配置放到bootstrap.yml中;

  • bootstrap.yml 文件中的配置在应用启动的早期阶段 (引导阶段) 就会被加载;
  • application.yml 文件在 PropertySourceList 中的位置在 bootstrap.yml 之前,优先级更高, 因此可以覆盖 bootstrap.yml 中的配置;
  • Nacos 配置中心的相关配置需要在应用启动的早期阶段加载,以便应用能够尽早连接到 Nacos 服务器获取配置。
spring:
  application:
    # 生成DataID需要
    name: facade-service
  profiles:
    # 生成DataID需要
    active: dev
  cloud:
    nacos:
      config:
        # 服务器地址
        server-addr: localhost:8848
        # 生成DataID需要
        file-extension: yaml
        # 指定group
        group: DEFAULT_GROUP
        # 指定namespace, 值应该取命名空间对应的ID, 而不是空间名
        namespace: eaf7318f-2016-4fb2-a37e-3d528087e550

对 Nacos 配置集中的配置, 可以直接注入并使用; 此时还无法实现配置的热更新; 修改Nacos中保存的配置后, 不会在服务中立即生效, 需要重启服务;

@Value("${nacos.test.config0}")
String nacosTestConfig0;

@GetMapping("/facade/nacosconfig")
public String nacosConfig() {
    return nacosTestConfig0;
}

在使用了配置的类上添加@RefreshScope注解开启热更新;

开启热更新后, 无需重启服务, 即可实现配置更新;

@RefreshScope
public class FacadeController {
    @Value("${nacos.test.config0}")
    String nacosTestConfig0;

    @GetMapping("/facade/nacosconfig")
    public String nacosConfig() {
        return nacosTestConfig0;
    }
}

持久化

Derby 是一个基于JAVA开发关系型数据库, 是可嵌入的。应用程序可以将 Derby 嵌入应用程序进程中,从而无需管理单独的数据库进程或服务。

我们在Nacos服务器上写入的配置,会被持久化保存到Nacos自带的derby中,因此当我们重启Nacos之后,仍然可以看到之前的配置信息。

Nacos还支持将配置信息写入Mysql中:

  1. 在Mysql中,创建名为nacos的数据库 ;

  2. 在nacos数据库中,执行 nacos 安装目录的 conf 目录下的 mysql-schema.sql脚本, 创建表结构;

  3. 修改 Nacos安装目录 conf/application.properties文件

    spring.datasource.platform=mysql
    db.num=1
    # 这里的url要改成你自己的mysql数据库地址,并在你的mysql中创建名为nacos的数据库
    db.url.0=jdbc:mysql://11.162.196.16:3306/nacos?
    characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
    # 这里要改成你自己登录mysql的用户名和密码
    db.user.0=nacos_devtest
    db.password.0=youdontkno
    

监听多个配置文件

假设 bootstrap.yaml 有如下配置, 那么 Nacos 客户端启动时, 不仅会监听order-service-dev.yaml, 还会自动监听order-serviceorder-service.yaml两个文件;

spring:
  application:
    name: order-service
  profiles:
    active: dev
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yaml

如果要监听更多配置文件, 例如公共的 MySQL 配置文件, 例如公共的 Redis 配置文件, 进行如下配置

server:
  port: 7770
spring:
  application:
    name: order-service
  profiles:
    active: dev
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yaml
        shared-configs:
          - data-id: mysql.yaml
            refresh: true
          - data-id: redis.yaml
            refresh: true

在这里插入图片描述

发布配置原理2.0.4

客户端

Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "localhost");
ConfigService configService = NacosFactory.createConfigService(properties);
configService.publishConfig("test-mysql.yaml", "DEFAULT_GROUP", "my: content");
  1. 客户端通过 NacosConfigService 向 Nacos 配置中心发出 ConfigPublishRequest请求;
  2. 这个请求里封装了命名空间, Group, dataId, 配置内容等信息;
  3. 2.X 版本, 默认是使用的 gRPC 的方式发送;

服务端

以下分析基于开启了 MySQL 外置存储的情况;

服务端由 ConfigPublishRequestHandler 来处理发布请求;

  1. 将命名空间, 分组, dataId, 内容等信息从请求中提取出来, 然后插入到数据库中;

  2. 然后发布一个ConfigDataChangeEvent, 这个事件被异步地处理, 将配置的发布同步给 Nacos 集群中的其它结点;

  3. 然后再异步地将配置作为一个文件持久化保存到服务器的文件系统中;

    在这里插入图片描述

  4. 然后根据命名空间, 组名和data-id去获取内存中, 当前发布的配置集对应的缓存CacheItem, 这个缓存保存了配置集的一些描述信息, 例如由配置项计算出的MD5值, 例如上次修改时间, 例如配置文件的类型(比如yaml)等, 如果没有, 就新创建一个缓存;

    磁盘文件存内容, 内存缓存存描述信息;

    MD5 值作为配置的一种版本标记; 当一个配置文件的 MD5 改变, 认为该文件发生了更新;

  5. 检查缓存的 MD5 值(如果是新建的, MD5 = null) 与最新的配置集内容计算出的MD5值是否相同, 如果不同, 发布一个LocalDataChangeEvent , 推送新发布的配置给订阅了这个配置的客户端;

拉取配置原理

基于2.0.4版本

客户端

  1. ApplicationContext 初始化之前, 会有一个独立于应用程序主上下文的 BootstrapContext 先被创建并初始化, 这个上下文读取的是 bootstrap.yaml 文件;

  2. 这个上下文用于引导配置属性源;

  3. 了解: 在主上下文的 prepareEnvironment 阶段, 会发布一个EnvironmentPrepared 事件; 这个事件会被BootstrapApplicationListener( 也是在 spring-cloud- context.spring.facories文件中声明的 ) 处理;
    它会创建并初始化一个 BootstrapContext

  4. BootstrapContext 初始化时, 会加载 spring.factories 文件中声明的 BootstrapConfiguration类型的组件, 进行自动配置;

  5. SpringCloud 官方团队在spring-cloud-context-3.1.4.jar\META-INF\spring.factories文件中提供了一个 BoostrapConfiguration, 叫PropertySourceBootstrapConfiguration; 同时, 它也是一个初始化器 ApplicationContextInitializer;

  6. 这个初始化器会自动注入 BootstrapContext 中的所有 PropertySourceLocator, 包括NacosPropertySourceLocator

  7. NacosPropertySourceLocator是由 Nacos提供的 NacosConfigBootstrapConfiguration 注册的, 这个配置类也是一个BootstrapConfiguration;

  8. 这个已经注入NacosPropertySourceLocator的初始化器PropertySourceBootstrapConfiguration 会放到SpringApplication对象中保存;

  9. 这样, 就通过 SpringApplication 对象在 BootstrapContext 和应用的主上下文之间传递了PropertySourceBootstrapConfiguration

  10. 应用的主上下文在 prepareContext 阶段, 调用 applyInitializers 时, 会调用所有初始化器的初始化方法, 这其中就包括PropertySourceBootstrapConfiguration的初始化方法; 这个初始化方法会遍历自己持有的PropertySourceLocator, 并调用他们的 locateCollection 方法

    为什么采用初始化器ApplicationContextInitializerBootstrapConfiguration的方式去发送拉取配置的请求? 因为要确保在ApplicationContext初始化之前就能拿到 Nacos 配置中心中的配置, 这样才能在refreshContext的时候去使用这些配置

  11. 所以, Nacos 自动配置类注入的 NacosPropertySourceLocator 就派上用场了;

  12. NacosPropertySourceLocator中, 从 Environment 中获取到 bootstrap.yaml 文件对应的 PropertySource, 进而拿到 Nacos 配置中心的地址, 和要拉取的配置文件的 DataId 等信息,

  13. 创建NacosConfigService, 调用其getConfig方法拉取配置;

  14. getConfig 方法中, 会首先尝试从本地的一个特定路径下读取配置文件, 这个文件目录中间有一级是data开头的;

  15. 但是, 客户端在生成本地缓存的时候, 是放到的snapshot开头的文件夹下; 所以除非你手动创建data开头的本地缓存, 这里永远读不到数据;

    根据 Github Issues 中 Nacos 作者的官方回答, 这是一种容灾机制, 防止连不上 Nacos 配置中心, 应用程序无法启动;

    连不上的时候, 你就手动创建data 开头的缓存目录, 然后把 snapshot 文件夹下自动保存的配置文件复制进去;

  16. 如果从本地缓存读到了, 直接返回, 但一般都是读不到;

  17. 如果读不到, 发送拉取配置的请求, 从 Nacos 服务器获取配置集;

  18. 将从服务器获取到的配置集保存到 snapshot 文件夹下;

  19. 将获取到的配置集封装成属性源, 保存到 Environment 中; 以后就可以使用了; 每一个 dataId, 都会对应一个属性源 PropertySource;

  20. 并且, 来自 Nacos 配置中心的属性源, 会在 Environment 内的 PropertySources 内的 List 中排最前;

  21. 另外, 这些来自Nacos 的属性源, 还会在这时被添加到一个 NacosPropertySourceRepository 中, 后面订阅的时候就是从这里取的要订阅的属性源, 获取Group和 DataId (为什么没有命名空间? 因为一个客户端获取的配置集的命名空间是固定的, 保存在一个固定的地方, 发请求的时候当然也会携带上命名空间);

服务端

  1. ConfigQueryRequestHandler中处理拉取请求;

  2. 默认情况下, 读取服务器本地的缓存文件, 而不是数据库; 读取后返回给客户端; 如果本地缓存没有读到, 也不会去数据库中读, 返回空;

    可以进行配置, 让服务器从数据库读取, 而不是本地缓存;

  3. 在服务器启动的时候, 会从数据库拉取配置信息, 保存到服务器文件系统中;

默认情况下, 手动修改数据库中的配置集内容, 只会影响到控制台, 不会影响到Nacos本地缓存, 也就不会改变客户端拉取配置的结果;

只有Nacos Server 重启的时候, 才会将数据库中的内容装载到服务器本地;

控制台

控制台显示的配置信息是直接从数据库中读取的;

这里强烈建议大家自己动手验证, 手动修改数据库中保存的配置, 然后去看控制台的显示和客户端拉到的配置是否一致;

这里是一个大坑, 有时候明明控制台看到的是 variable = a, 但项目中读到的可能是 variable = b;

订阅配置

客户端发起订阅

  1. 和拉取配置不同, 你得先准备好了 Web 容器, 才能去订阅配置, 所以应用启动时自动订阅配置, 是由 Nacos-Config 的普通自动配置类向 Spring 容器中注册了一个 ApplicationListener, 叫NacosContextRefresher;
  2. 由这个监听器, 监听 ApplicationReady 事件 ( 这个事件是在 callRunners 之后发布的 );
  3. 监听到之后, 就会遍历 NacosPropertySourceRepository, 获取每个 Nacos 属性源的组名和DataId, 然后注册与之对应的监听器:
String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
Listener listener = listenerMap.computeIfAbsent(key,
        // 回调函数                                        
		lst -> new AbstractSharedListener() {
			@Override
			public void innerReceive(String dataId, String group,
					String configInfo) {
				refreshCountIncrement();
				nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
				applicationContext.publishEvent(
						new RefreshEvent(this, null, "Refresh Nacos config"));
				if (log.isDebugEnabled()) {
					log.debug(String.format(
							"Refresh Nacos config group=%s,dataId=%s,configInfo=%s",
									group, dataId, configInfo));
						}
					}
				});
try {
    // 向服务器注册监听器;
	configService.addListener(dataKey, groupKey, listener);
	log.info("[Nacos Config] Listening config: dataId={}, group={}", dataKey,
			groupKey);
}

服务器处理监听请求

  1. 抽取出客户端的 ConnectionId, 和要监听的文件的标识 groupKey, 虽然叫groupKey, 但实际是DataId-Group-namespace构成的;

在这里插入图片描述

  1. ConfigChangeListenContext中维护订阅关系;
public class ConfigChangeListenContext {
    
    /**
     * <GroupKey, Set<监听了这个GroupKey的ConnnectId>>
     */
    private ConcurrentHashMap<String, HashSet<String>> groupKeyContext = new ConcurrentHashMap<String, HashSet<String>>();
    
    /**
     * <客户端ID, Map<这个客户端监听的GroupKey, 配置的MD5>值
     */
    private ConcurrentHashMap<String, HashMap<String, String>> connectionIdContext = new ConcurrentHashMap<String, HashMap<String, String>>();   
}

服务器推送更新

在这里插入图片描述

  • 这个事件被RpcConfigChangeNotifier监听器处理, 从事件中提取出对应的配置集的 DataId, group, groupKey 等等信息;
  • 从维护监听关系的对象ConfigChangeListenContext中, 根据 GroupKey取出监听了这个配置集的所有客户端的ConnectionId;
  • 遍历所有 ConnectionId, 将新的配置, 封装成一个配置变化的通知请求, 通过 RPC 工具将请求 Push 给客户端;

客户端热更新

使用 Java API 监听某个来自 Nacos 的配置文件, 配置文件重新发布后, Environment 中的 PropertySource 确实更新了, 但是使用 @Value 注解注入了某个属性值的Bean, 他的属性值没有改变; 因为 @Value 通过反射将属性值注入后, 这个属性值就保存在 Bean 中了, 修改 Environment 不会影响到它;

Nacos 采取的策略是将配置了热更新 ( @RefreshScope ) 的 Bean 销毁, 重新创建;

客户端收到配置变化的通知请求后, 发布一个刷新事件

String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
Listener listener = listenerMap.computeIfAbsent(key,
        // 回调函数                                        
		lst -> new AbstractSharedListener() {
			@Override
			public void innerReceive(String dataId, String group,
					String configInfo) {
				refreshCountIncrement();
				nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
				applicationContext.publishEvent(
						new RefreshEvent(this, null, "Refresh Nacos config"));
				if (log.isDebugEnabled()) {
					log.debug(String.format(
							"Refresh Nacos config group=%s,dataId=%s,configInfo=%s",
									group, dataId, configInfo));
						}
					}
				});
try {
    // 向服务器注册监听器;
	configService.addListener(dataKey, groupKey, listener);
	log.info("[Nacos Config] Listening config: dataId={}, group={}", dataKey,
			groupKey);
}

这个刷新事件最终来到ContextRefresher进行处理, 做了两个重要操作

一是更新 Environment 里面对应的 PropertySource, 更新完以后会发布一个EnvironmentChangeEvent, 是一个扩展点; 例如有一个监听器监听到这个事件后, 会更新 @ConfigurationProperties 注解修饰的参数类;

二是把有@RefreshScope注解修饰的 Bean 销毁重建; 完成后发布一个RefreshScopeRefreshedEvent, Gateway 就是通过对这个事件的监听, 完成路由配置的热更新;

重建bean
  1. @RefreshScope注解的本质是@Scope("refresh"); 一个 Bean 的 Scope 可以是 singleton, 可以是 prototype, 可以是 Session (每个 Session 创建一个新的 Bean); 在创建 bean的时候, 会根据 BeanDefinition 中的 Scope 进行不同的创建逻辑;
  2. 总得来说: 所有 Scope 为 refresh 的 Bean, 创建时都会被放到同一个缓存中;
  3. 更新 Environment 后, 发布一个环境更新事件, 由ConfigurationPropertiesRebinder处理这个事件;
  4. 销毁 refreshScope 缓存中的 bean, 然后重新初始化这些被销毁的 bean;
安全问题
  1. 销毁时加锁, 防止多个线程同时销毁;
  2. 销毁之前, 创建一个代理, 在新的 bean 没有准备好之前, 用这个代理;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1992226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s 与 docker 安装 Syncthing 文件同步服务器

Syncthing是一个开源文件同步工具&#xff0c;可以在多台设备之间实时同步文件或文件夹&#xff0c;官方网站&#xff1a;https://syncthing.net/ 下载地址&#xff1a;https://syncthing.net/downloads/ &#xff0c;如果是windows一般推荐下载图形界面SyncTrayzor, 但我这边都…

视频循环存储的实现

目录 1. 三方工具 2. 视频存储的实现 2.1 分段存储 - 比如每15分钟 2.2 对齐到15分钟整边界 2.3 循环存储的实现 video_space_daemon.sh 3.封装 3.1 主执行程序&#xff0c;修订版 3.2 创建服务 3.3 service关联的执行脚本文件 4.额外的工作 附录A: ffmpeg视频存储…

中电金信三步法全面助力银行数字化营销体系建设

存量用户竞争时代 精细化经营、个性化服务与多场景覆盖 成为银行经营的重要策略 营销数字化转型不可或缺 但是&#xff0c;与所有转型的曲折、阵痛等特征一样&#xff0c;银行构建数字化营销运营体系过程中&#xff0c;亦走过一些弯路&#xff0c;包括&#xff1a; 缺少顶层…

收银系统源码-连锁店版本

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 私有化独立部署/全开源源码&#xff0c;系统开发语言&#xff1a; 核心开发语言: PHP、HTML…

【漏洞复现】某赛通电子文档安全管理系统 PolicyAjax SQL注入漏洞

0x01 产品简介 某赛通电子文档安全管理系统&#xff08;简称&#xff1a;CDG&#xff09;是一款电子文档安全加密软件&#xff0c;该系统利用驱动层透明加密技术&#xff0c;通过对电子文档的加密保护&#xff0c;防止内部员工泄密和外部人员非法窃取企业核心重要数据资产&…

医药行业如何对内部机密数据进行加密保护?

一、医药行业加密解决方案 1、药品研发过程中加密保护重要数据&#xff0c;防止信息泄露 考虑到药品研发部门数据安全重要性高&#xff0c;且与其他部门较少业务关系往来&#xff0c;为防止公司研发配方泄密到其他部门或者公司外&#xff0c;研发部门实行权限控制。做到研发部…

JESD204B/C协议学习笔记

JESD204B基础概念 204B包含传输层&#xff0c;链路层&#xff0c;物理层。 应用层是对 JESD204B 进行配置的接口&#xff0c;在标准协议中是不含此层&#xff0c;只是为了便于理解&#xff0c;添加的一个层。 协议层指工程中生成的IP核JESD204B&#xff0c;负责处理输入的用户…

pod详解 list-watch机制 预选优选策略 如何指定节点调度pod

K8S是通过 list-watch 机制实现每个组件的协同工作 controller-manager、scheduler、kubelet 通过 list-watch 机制监听 apiserver 发出的事件&#xff0c;apiserver 也会监听 etcd 发出的事件 scheduler的调度策略&#xff1a; 预选策略&#xff08;Predicates&#xff09;…

JavaScript中的__setitem__方法

1、问题背景 Python中存在一个名为__setitem__的方法&#xff0c;该方法能够在向对象中设置值时对其进行处理。例如&#xff0c;以下代码演示了如何在Python中使用__setitem__方法对一个字典中的键值对进行平方处理&#xff1a; class CustomDict(dict):def __setitem__(self…

PMP证书3A一次通过攻略(内含血泪教训和踩过的坑,避雷必看)

手把手教你如何3A速通PMP证书 首先&#xff1a;你考PMP到底图什么&#xff1f; 清晰、强烈的动机是意志力的基础&#xff0c;你是想要加薪升职&#xff1f;转行转岗&#xff1f;还是只是为了知识&#xff1f;无论什么理由都可以&#xff0c;当搞明白到底图什么&#xff0c;学习…

URDF在线可视化网站

​ URDF全称&#xff08;United Robotics Description Format&#xff09;统一机器人描述格式&#xff0c;是一个XML语法框架下用来描述机器人的语言格式&#xff0c;URDF在ROS界很流行。 关于URDF的详细介绍&#xff0c;请参考博文URDF学习&#xff08;一&#xff09;什么是U…

海康gige工业相机无驱动取像突破(c#实现,版本更新,你也可以移植到linux下去用)

我们前面有一个版本&#xff0c;没有整理&#xff0c;只能是500万海康gige工业相机无驱动取像成功&#xff08;黑白相机gm&#xff09;。 这里&#xff0c;版本更新了&#xff0c;可以不是500万&#xff0c;200万&#xff0c;80万也可以&#xff0c;你可以去试一试&#xff0c…

【C++二分查找 树状数组】2424. 最长上传前缀

本文涉及的基础知识点 C二分查找 树状数组 LeetCode2424. 最长上传前缀 给你一个 n 个视频的上传序列&#xff0c;每个视频编号为 1 到 n 之间的 不同 数字&#xff0c;你需要依次将这些视频上传到服务器。请你实现一个数据结构&#xff0c;在上传的过程中计算 最长上传前缀…

“AI+Security”系列第2期(一):对抗!大模型自身安全的攻防博弈

近日&#xff0c;由安全极客、Wisemodel 社区和 InForSec 网络安全研究国际学术论坛联合主办的“AISecurity”系列第 2 期——对抗&#xff01;大模型自身安全的攻防博弈线上活动如期举行。本次活动邀请了君同未来创始人兼 CEO 韩蒙、前阿里云高级安全专家郑瀚、ChaMd5 AI 组负…

浏览器突然无法正常访问网页的解决方案

文章目录 1.问题描述&#xff1a;2.解决方案3.总结 1.问题描述&#xff1a; 浏览器之前是可以正常搜索的&#xff0c;但是这次打开显示无法正常访问&#xff0c;但是部分网页又是可以正常访问的 2.解决方案 1.搜索控制面板 2.点击网络和Internet 3.点击网络共享中心 4.点击…

大数据面试SQL(五):查询最近一笔有效订单

文章目录 查询最近一笔有效订单 一、题目 二、分析 三、SQL实战 四、样例数据参考 查询最近一笔有效订单 一、题目 现有订单表t5_order&#xff0c;包含订单ID&#xff0c;订单时间&#xff0c;下单用户&#xff0c;当前订单是否有效。 请查询出每笔订单的上一笔有效订…

Fanuc机床的数据采集解决方案

对于制造业而言随着工业数字化的发展&#xff0c;完善工厂信息化管理&#xff0c;消除信息孤岛是越来越多工控人关注的事情。其中不得不提到的就是CNC系统&#xff0c;科学的程序管理和规范可以减少机床辅助时间&#xff0c;提高机床利用效率&#xff1b;及时准确的获取机床实时…

线程池c代码实现

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、线程池是什么&#xff1f;二、代码示例总结 前言 线程池c代码简单实现&#xff1a; 大致思路如下&#xff1a; 一个管理线程轮询工作线程是否空闲&#xf…

程序员如何在人工智能时代保持核心竞争力

目录 1.概述 1.1. 技术深度与广度的平衡 1.2. 软技能的培养 1.3. 持续学习和适应性 1.4. 理解和应用AI 1.5. 伦理和责任意识 2.AI辅助编程对程序员工作的影响 2.1.AI工具对编码实践的积极影响 2.2.AI工具的潜在风险 2.3.如何平衡利与弊 3.程序员应重点发展的核心能力…

RabbitMQ docker安装

后台配置文件 rabbitmq:image: rabbitmq:latestcontainer_name: rabbitmqports:- "5672:5672" # RabbitMQ server port- "15672:15672" # RabbitMQ management console portenvironment:RABBITMQ_DEFAULT_USER: adminRABBITMQ_DEFAULT_PASS: admin 若要打…