《Nacos 2.x源码深度解析》专栏目录

一、架构通信篇:

《Nacos 2.x 源码深度解析 (一):架构整体全貌 —— 核心模块划分与版本演进》

《Nacos 2.x 源码深度解析 (二):通信协议迭代 —— HTTP长轮询到gRPC演进》

二、配置中心篇

《Nacos 2.x 源码深度解析 (三):配置中心客户端 —— 启动加载与自动装配》

《Nacos 2.x 源码深度解析 (四):配置中心服务端 —— 事件总线与数据持久化》

《Nacos 2.x 源码深度解析 (五):gRPC 推送链路 —— 配置变更下发与动态刷新》

《Nacos 2.x 源码深度解析 (六):三级缓存体系 —— 降级兜底与故障自愈机制》

三、服务注册发现篇

《Nacos 2.x 源码深度解析 (七):服务注册流程 —— 客户端上报与服务端存储》

《Nacos 2.x 源码深度解析 (八):服务订阅机制 —— 从首次订阅到gRPC双向流变更通知》

《Nacos 2.x 源码深度解析 (九):双向流设计 —— 连接创建复用与销毁》


目录

一、示例代码:本地启动客户端

二、自动配置入口:从Spring Boot启动说起

2.1 引导上下文加载:spring-cloud-starter-bootstrap父子上下文机制

2.2 Nacos接入核心:starter依赖与自动装配体系

2.3 动态刷新的起点:NacosConfigRefreshEventListener

2.4 监听器的注册入口:NacosContextRefresher

2.5 远程配置加载核心:ConfigRpcTransportClient

2.6 底层通信入口:ConfigService

2.7 整体链路:依托自动装配完成初始化闭环

三、全文小结


在上一篇文章中,我们深入分析了 Nacos 2.x 通信层的协议演进与 gRPC 源码实现,理解了 Client 与 Server 之间如何通过一条长连接承载所有通信。本篇将视角转向客户端,聚焦配置中心在 Spring Boot 应用启动阶段的初始化全流程。

客户端启动看似简单,引入依赖、加点配置、启动应用,配置就自动从 Nacos 拉下来了。但这背后其实是一套精密的自动装配体系在运转:Bootstrap 父子上下文如何确保远程配置优先加载?NacosConfigDataLoader 如何通过 gRPC 从服务端拉取配置并注入 EnvironmentNacosContextRefresher 又如何为每个配置注册监听器,打通从服务端推送到客户端感知的事件通道?NacosConfigRefreshEventListener 又如何将 Nacos 事件桥接为 Spring Cloud 标准 RefreshEvent,触发 @RefreshScope Bean 的无停机热刷新?

本文将从 spring-cloud-starter-bootstrap 的父子上下文机制切入,沿着 NacosConfigDataLocationResolverNacosConfigDataLoaderConfigServiceNacosContextRefresherNacosConfigRefreshEventListener 的调用链路,完整呈现配置中心客户端在启动阶段的初始化全貌。理解了这套机制,就掌握了 Nacos 配置管理在应用侧落地的基础骨架。

一、示例代码:本地启动客户端

为了帮助大家能更好的理解《Nacos 2.x 源码深度解析》的内容,作者根据文章的核心内容编写了示例代码。该示例代码已经完成了基础依赖bom的搭建,使用者无需关心依赖兼容问题。

示例代码地址:https://github.com/Yuuu97/nacos-examples

示例代码说明:

本文配套示例代码采用父子 POM 的结构进行依赖管理,每篇文章均有对应的独立模块及说明文件。拉取代码后,建议先阅读父子项目的 README.md,了解整体结构与运行说明。示例代码的定位是辅助读者快速理解每篇文章所讲解的核心内容,请以文章的分析脉络为主线、示例代码为辅,重点厘清各模块的设计思想和执行流程。作者在编写示例时力求结构简洁、易于阅读,所有代码均为业余时间完成,若有不完善之处还望读者海涵,也欢迎大家提出宝贵意见,互相交流学习。

运行环境:

Spring Boot版本:3.2.5

JDK17

Maven 3.9.14

启动客户端:

请先参照第一篇《架构整体全貌——核心模块划分与版本演进》中的指引,在本地启动 Nacos 2.4.3 源码。随后登录 Nacos 控制台,在 bootstrap.yml 所配置的命名空间下新建 application-dev.ymldynamic-config.yml 两个配置文件,按需填入测试配置项。完成上述准备后运行示例代码,即可断点调试应用启动阶段 Nacos 加载远程配置的完整流程。

二、自动配置入口:从Spring Boot启动说起

Spring Boot启动类通过@SpringBootApplication注解中的@EnableAutoConfiguration,触发自动配置机制,扫描依赖包下的spring.factories文件以及spring目录下的org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。这两者的区别在于,spring.factories可以配置Spring启动时装载的所有类型的Bean,而AutoConfiguration.imports则专注于AutoConfiguration类型的Bean加载。

2.1 引导上下文加载:spring-cloud-starter-bootstrap父子上下文机制

spring-cloud-starter-bootstrap的核心是Marker类,一个空的标记类,不包含任何逻辑代码。该 starter 的唯一作用就是将它引入应用的类路径中。Spring Boot 启动时,spring-cloud-context包下的BootstrapApplicationListener会检测类路径中是否存在这个Marker类,若存在则等价于显式配置了spring.cloud.bootstrap.enabled=true,从而开启 Bootstrap 引导机制。一旦引导开关生效,BootstrapApplicationListener会在主容器刷新之前预先创建一个独立的BootstrapContext父上下文,优先加载bootstrap.ymlbootstrap.properties中的前置配置(如配置中心地址、注册中心凭证、环境标识等),并通过BootstrapPropertySourceLocator将这些配置注入全局Environment并赋予最高优先级。主容器初始化时,这些前置配置已就绪,且不会被application.yml中的同名配置覆盖,保证配置中心连接参数等关键信息始终以 Bootstrap 阶段加载的为准。

2.2 Nacos接入核心:starter依赖与自动装配体系

spring-cloud-starter-alibaba-nacos-config负责初始化与Nacos集成的基础设施,是Spring Cloud应用与Nacos Config Server实现配置中心化、动态化的核心桥梁。它首先检查环境变量中spring.config.import是否包含nacos相关配置,若未指定导入来源或未声明任何Nacos Server上的配置文件,启动阶段便会抛出异常,以此避免应用在配置缺失的情况下“裸启动”,保障配置加载的完整性。

校验通过后,自动装配机制会触发加载NacosConfigSpringCloudAutoConfiguration配置类,完成NacosConfigManager实例的初始化——该实例是客户端与Nacos Server交互的核心管理类,负责维护Nacos客户端连接、配置拉取与监听的核心逻辑,并在上下文就绪时注册ApplicationEvent事件,为后续配置动态刷新奠定基础。

这里注入的NacosConfigRefreshEventListener正是后续实现动态刷新的关键——当Nacos Server端配置发生变更时,Nacos只负责通过gRPC双向流将变更通知推送到Client端,而运行时配置真正生效,依赖的则是Spring Cloud的RefreshEvent机制:监听器捕获事件后,触发Environment的配置属性重新加载,进而通过@RefreshScope等注解刷新持有配置的Bean,完成从“收到变更通知”到“配置实际生效”的完整闭环,实现无重启更新配置。

spring-alibaba-nacos-config是初始化Nacos的核心模块,其关键在于spring.factories文件中声明的两个入口类:NacosConfigDataLocationResolverNacosConfigDataLoader,二者协同完成远程配置的定位与拉取,这个过程依赖Bootstrap上下文的优先执行(需配合spring-cloud-starter-bootstrap启用,确保配置拉取早于业务Bean初始化)。

NacosConfigDataLocationResolver负责解析配置路径,将bootstrap.yml中spring.cloud.nacos.config相关配置信息,转换为可寻址的配置源,明确客户端需要从Nacos Server拉取的具体配置资源;而NacosConfigDataLoader则是本小节重点研究的对象,启动时从Nacos拉取配置的秘密,就封装在它的load方法之中。该方法通过Nacos客户端向Server端发起请求,拉取对应Data Id和Group的配置内容,解析为Spring可识别的PropertySource,并注入全局Environment环境变量,且其配置优先级高于本地application.yml,确保远程配置能覆盖本地同名基础配置,实现配置的统一管理。

2.3 动态刷新的起点:NacosConfigRefreshEventListener

NacosConfigSpringCloudAutoConfiguration自动装配中,会注册NacosConfigRefreshEventListener监听器,它是连接 Nacos 配置变更与Spring Cloud刷新机制的核心桥梁。当Nacos服务端配置发生变更并推送到客户端时,NacosContextRefresher内部的配置监听器会感知到变化,并发布Nacos 自定义事件 NacosConfigRefreshEventNacosConfigRefreshEventListener作为该事件的监听器会被立即触发,它的核心是做两件关键事情:

  • 事件转换:将 Nacos 自定义的 NacosConfigRefreshEvent 转换成 Spring Cloud 标准的 RefreshEvent

  • 事件广播:通过 applicationContext.publishEvent()将标准RefreshEvent发布到 Spring 容器。

    之所以需要这层转换,原因在于事件类型的兼容性。因为NacosConfigRefreshEvent 是Nacos私有的自定义事件,Spring Cloud体系并不识别,而RefreshEvent是 Spring Cloud 定义的标准刷新事件,Spring Cloud的核心能力(如 @RefreshScope 动态刷新、配置绑定更新)只监听标准 RefreshEvent,不会识别 Nacos 私有的事件。因此,NacosConfigRefreshEventListene本质上就是一个翻译官,把Nacos私有的配置变更事件,翻译成 Spring Cloud 能识别的标准刷新事件,从而让整个Spring Cloud动态刷新机制正常运转。

 com.alibaba.cloud.nacos.NacosConfigSpringCloudAutoConfiguration
  
  // 创建NacosConfigRefreshEventListener实例
  @Bean(name = "nacosConfigSpringCloudRefreshEventListener")
  public NacosConfigRefreshEventListener nacosConfigRefreshEventListener() {
    return new NacosConfigRefreshEventListener();
  }
  
    ——————————————————————————————————————————————————————————————————————————————
  
  com.alibaba.cloud.nacos.configdata.NacosConfigRefreshEventListener#onApplicationEvent
  
  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    applicationContext.publishEvent(new RefreshEvent(event.getSource(), null, "Refresh Nacos config"));
  }

2.4 监听器的注册入口:NacosContextRefresher

在NacosConfigAutoConfiguration自动装配中会初始化NacosContextRefresher,NacosContextRefresherNacosConfigAutoConfiguration 通过 @Bean初始化,构造器注入 NacosConfigManagerNacosRefreshHistory,并从配置中获取 refreshEnabled 开关,控制动态刷新是否启用。

容器启动后,registerNacosListenersForApplications方法开始执行:先判断刷新开关状态,开启后遍历所有已加载的 NacosPropertySource,跳过不可刷新配置,为每个有效dataId + group调用registerNacosListener 注册监听器。

registerNacosListenerdataId+group 为唯一 key,通过listenerMap避免重复创建监听器。内部构建 AbstractSharedListener 实例,重写 innerReceive 回调方法:配置变更时,执行刷新次数累加、记录刷新历史、保存配置快照,最终构造并发布NacosConfigRefreshEvent事件。

最后调用configService.addListener完成订阅,底层基于gRPC双向长连接与Nacos服务端通信,服务端配置变更后会主动推送通知,触发回调并完成Spring事件发布。

com.alibaba.cloud.nacos.NacosConfigAutoConfiguration#nacosContextRefresher
​
@Bean  // 创建NacosContextRefresher实例
  public NacosContextRefresher nacosContextRefresher(NacosConfigManager nacosConfigManager,
      NacosRefreshHistory nacosRefreshHistory) {
    return new NacosContextRefresher(nacosConfigManager, nacosRefreshHistory);
  }
  
      ——————————————————————————————————————————————————————————————————————————————
        
com.alibaba.cloud.nacos.refresh.NacosContextRefresher
        
  // 构造器注入NacosConfigManager和NacosRefreshHistory
  public NacosContextRefresher(NacosConfigManager nacosConfigManager,
      NacosRefreshHistory refreshHistory) {
    this.configManager = nacosConfigManager;
    this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
    this.nacosRefreshHistory = refreshHistory;
    this.isRefreshEnabled = this.nacosConfigProperties.isRefreshEnabled();
  }
​
// 为所有已加载的NacosPropertySource注册监听器
private void registerNacosListenersForApplications() {
    if (isRefreshEnabled()) {
      for (NacosPropertySource propertySource : NacosPropertySourceRepository
          .getAll()) {
        if (!propertySource.isRefreshable()) {
          continue;
        }
        String dataId = propertySource.getDataId();
        registerNacosListener(propertySource.getGroup(), dataId);
      }
    }
  }
​
private void registerNacosListener(final String groupKey, final String dataKey) {
    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();
            // 将本次刷新的 dataId、group、新内容、时间戳写入历史记录
            nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
            // NacosSnapshotConfigManager保存一份配置的快照,用于配置回滚、对比差异等场景
            NacosSnapshotConfigManager.putConfigSnapshot(dataId, group,
                configInfo);
            // 创建一个NacosConfigRefreshEvent,携带dataId和group信息
            NacosConfigRefreshEvent event = new NacosConfigRefreshEvent(this, null, "Refresh Nacos config");
            event.setDataId(dataId);
            event.setGroup(group);
            // 发布事件,由NacosConfigRefreshEventListener监听
            applicationContext.publishEvent(
                event);
          }
        });
      // 调用 ConfigService.addListener(),底层通过gRPC双向流通道向服务端注册,服务端会在该配置变更时,通过长连接推送通知给当前客户端
      configService.addListener(dataKey, groupKey, listener);
  }

2.5 远程配置加载核心:ConfigRpcTransportClient

NacosConfigDataLoaderSpring Boot启动阶段从Nacos Server拉取配置的核心实现类。它的职责是从Spring Context中获取前面自动装配好的NacosConfigPropertiesNacosConfigManager,两者携带着连接Nacos Server所需的全部元信息——服务端地址、命名空间、分组、文件扩展名等。

拉取动作发生在load方法中,其内部调用链路如下:NacosConfigDataLoader.load()NacosConfigManager.getConfigService() 获取ConfigService实例 → 调用configService.getConfig()方法。在getConfig()内部,Nacos Client并非每次都直接请求远程服务端,而是优先检查本地故障转移文件和本地快照缓存,当本地不存在时,才通过gRPC双向流向Server发起远程查询。这种本地优先、远程兜底的策略,既保证了首次启动时配置的可获取性,也确保了在服务端短暂不可用时客户端依然能基于上次缓存的配置完成启动。

拉取到对应环境的配置内容后(如application-prod.yml),Nacos会将其封装为PropertySource,注册到NacosPropertySourceLocator管理的全局仓库中,以dataId为唯一标识便于后续查找和管理。最终,所有拉取的配置被组装成ConfigData对象返回,由Spring Boot合并到运行时的Environment中,供后续Bean创建和依赖注入使用。

com.alibaba.cloud.nacos.configdata.NacosConfigDataLoader
​
public ConfigData doLoad(ConfigDataLoaderContext context,
        NacosConfigDataResource resource) {
    // 从Spring context里获取NacosConfigManager,再通过其拿到ConfigService
    ConfigService configService = getBean(context, NacosConfigManager.class)
            .getConfigService();
    
    // 获取NacosConfigProperties,其封装了 spring.cloud.nacos.config.*下的所有属性(如server-addr、namespace)
    NacosConfigProperties properties = getBean(context,
            NacosConfigProperties.class);
​
    // config里包含dataId配置标识、group配置分组、suffix配置文件后缀(如properties、yaml、json)、refreshEnabled是否开启自动刷新
    NacosItemConfig config = resource.getConfig();
    
    // 向Nacos Server发起请求,拉取指定dataId + group的配置内容
    List<PropertySource<?>> propertySources = pullConfig(configService,
            config.getGroup(), config.getDataId(), config.getSuffix(),
            properties.getTimeout());
​
    // NacosPropertySource是Nacos对Spring PropertySource的扩展
    NacosPropertySource propertySource = new NacosPropertySource(propertySources,
            config.getGroup(), config.getDataId(), new Date(),
            config.isRefreshEnabled());
​
    // 注册到全局仓库,将所有加载过的Nacos配置集中管理,方便后续查找和管理,该仓库主要用于
    // 1、配置刷新时,快速定位需要更新的 PropertySource
    // 2、运行时查找所有 Nacos 配置源
    // 3、避免同一个 dataId 被重复加载
    NacosPropertySourceRepository.collectNacosPropertySource(propertySource);
​
    // 构建Spring Boot ConfigData,Spring Boot会将这些PropertySource按优先级合并到Environment中,合并后,应用代码就可以通过 @Value、@ConfigurationProperties 等方式使用配置了
    return new ConfigData(propertySources, getOptions(context, resource));
}

2.6 底层通信入口:ConfigService

ConfigService是配置中心的核心接口,这个接口是Nacos SDK中配置管理功能的入口,定义了应用从 Nacos 获取、监听、发布配置的全部操作,提供了配置拉取、配置监听、配置发布、配置删除等核心能力。在 Nacos 2.x 中,默认实现类为 NacosConfigService,底层通过gRPC长连接与服务端通信。

com.alibaba.nacos.api.config.ConfigService
​
public interface ConfigService {
    // 从Nacos服务端拉取配置内容(一次性操作,不会监听后续变更)
    String getConfig(String dataId, String group, long timeoutMs) throws NacosException;
​
     // 获取配置并同时注册监听器,相较于调 getConfig() 再调 addListener()来说,该方法原子性更好,避免了  拿到配置后到注册监听前 这段时间内发生的变更丢失。
    String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener)
            throws NacosException;
​
     // 为指定配置添加监听器,实现配置的热更新。执行流程如下:
     // 1、客户端向基于dataId和group向Nacos服务端注册;
     // 2、当管理员在Nacos控制台修改了这个配置后:
     // 2.1 服务端通过 gRPC双向流向客户端推送变更通知
     // 2.2 客户端收到通知后,自动拉取最新配置内容
     // 2.3 回调 listener.receiveConfigInfo(newContent) 方法
    void addListener(String dataId, String group, Listener listener) throws NacosException;
   
     // 向Nacos服务端发布一条配置,该方法发布的是纯文本内容,Nacos 默认不会校验格式合法性,需要调用方自行保证内容格式正确(如 JSON 的逗号、括号匹配等)
    boolean publishConfig(String dataId, String group, String content) throws NacosException;
​
     // 向Nacos服务端发布配置,并指定配置类型,Nacos控制台会按对应格式进行语法高亮和校验
    boolean publishConfig(String dataId, String group, String content, String type) throws NacosException;
​
     // 使用 CAS(Compare-And-Swap,比较并交换)机制发布配置,多人同时编辑同一个配置时,避免后提交的人覆盖前一个人的修改。(不带类型)执行流程如下
     // 1、客户端先 getConfig() 获取当前内容,计算其MD5值
     // 2、编辑配置内容
     // 3、调用 publishConfigCas(),传入编辑前内容的MD5(casMd5)
     // 4、服务端对比当前内容的MD5和传入的casMd5,如果相同说明没人改过,允许更新;否则说明有人抢先改了,拒绝更新
    boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException;
  
    // CAS 发布配置(带类型)
    boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type)
            throws NacosException;
​
    // 从Nacos服务端删除一条配置
    boolean removeConfig(String dataId, String group) throws NacosException;
​
    // 移除之前注册的配置监听器
    void removeListener(String dataId, String group, Listener listener);
​
    // 获取Nacos服务端健康状态
    String getServerStatus();
​
    // 添加配置过滤器,该功能用于在配置拉取、发布等操作的前后插入自定义逻辑,如对配置内容进行加密/解密、添加自定义的校验规则等
    void addConfigFilter(IConfigFilter configFilter);
​
    // 关闭 ConfigService,释放底层资源。执行流程如下:
    // 1、移除所有已注册的监听器
    // 2、关闭与Nacos 服务端的gRPC连接
    // 3、释放线程池等资源
    void shutDown() throws NacosException;
}

2.7 整体链路:依托自动装配完成初始化闭环

Spring Cloud Nacos配置客户端全程基于Spring Boot自动装配机制,从启动引导、配置拉取、监听器注册到动态刷新就绪,形成一套完整、自动化、无侵入的初始化流程。

应用启动时,spring-cloud-starter-bootstrap开启引导上下文,优先加载bootstrap.yml中的Nacos连接信息,保证配置中心参数在主容器启动前就绪。随后Spring Boot自动装配触发Nacos自动配置类加载:NacosConfigAutoConfiguration初始化核心连接管理器 NacosConfigManagerNacosConfigSpringCloudAutoConfiguration 注册动态刷新事件监听器 NacosConfigRefreshEventListener

在配置加载阶段,NacosConfigDataLocationResolver解析配置定位,NacosConfigDataLoader通过 ConfigService 向 Nacos 服务端发起 gRPC 请求,拉取对应dataId + group的配置,封装为NacosPropertySource存入仓库并注入Spring Environment,优先级高于本地配置,确保远程配置优先生效。

配置拉取完成后,NacosContextRefresher作为监听器注册入口,在容器就绪后遍历所有可刷新配置,通过 configService.addListener为每个配置建立 gRPC 双向长连接监听,同时在回调中实现刷新计数、历史记录、快照保存,并发布 NacosConfigRefreshEvent 事件。

最终,NacosConfigRefreshEventListener 接收 Nacos 自定义事件,转换为 Spring Cloud 标准 RefreshEvent 并广播,触发 @RefreshScope Bean 刷新,完成配置拉取 → 监听订阅 → 变更感知 → 事件转发 → 动态生效的全链路闭环,实现应用无重启配置自动更新。

三、全文小结

本文聚焦配置中心客户端的启动加载与自动装配机制,从 Spring Boot 自动装配入口到 gRPC 长连接监听注册,详细分析了客户端初始化的全链路源码实现。

在启动引导层面,spring-cloud-starter-bootstrap 通过 Marker 类触发 BootstrapApplicationListener,在主容器刷新前预先创建 BootstrapContext 父上下文,优先加载 bootstrap.yml 中的配置中心地址、命名空间等前置参数,并赋予最高优先级,确保远程配置连接信息不被 application.yml 覆盖。在自动装配层面,spring-cloud-starter-alibaba-nacos-config 校验 spring.config.import 配置后,触发 NacosConfigSpringCloudAutoConfiguration 完成 NacosConfigManager 初始化与 NacosConfigRefreshEventListener 注册;spring-alibaba-nacos-config 则通过 NacosConfigDataLocationResolver 解析配置路径,由 NacosConfigDataLoader 调用 ConfigService 向服务端发起 gRPC 请求拉取配置,封装为 NacosPropertySource 注入 Environment,优先级高于本地配置。

在动态刷新层面,NacosContextRefresher 遍历所有可刷新配置,通过 configService.addListener() 为每个配置建立 gRPC 双向流监听,回调中发布 NacosConfigRefreshEventNacosConfigRefreshEventListener 将 Nacos 自定义事件桥接为 Spring Cloud 标准 RefreshEvent 并广播,触发 @RefreshScope Bean 热刷新,完成从配置拉取、监听注册、变更感知到动态生效的全链路闭环。

客户端的初始化骨架搭建完毕后,下篇将转向服务端,深入拆解 Nacos 配置中心的事件总线机制与数据持久化设计——NotifyCenter 如何实现发布订阅解耦,ConfigCacheService 如何构建内存与磁盘的双存储保障,以及 AsyncNotifyService 如何在集群节点间完成配置变更的实时同步。


原创不易,如果本文对您有帮助,带来了些许灵感或启发,烦请动动小手点赞、关注、转发、收藏。这是作者持续更新的动力源泉,衷心感谢您的支持。我会尽量在工作之余,为大家带来更高质量的内容,努力保持周更。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐