⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 苏三说技术 「苏三」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

前言

配置管理这个看似不起眼的环节,一旦出现问题,轻则功能异常,重则全站崩溃。

正因如此,配置中心成为了微服务架构中的标配。

而说到配置中心,目前国内最热门的两个选择非Nacos和Apollo莫属。

很多团队在选型时都会纠结:到底哪个更好用?

今天这篇文章就专门跟大家聊聊这个话题,希望对你会有所帮助。

01 设计理念

在深入技术细节之前,我们先看看这两个框架的“出身”和设计理念,这决定了它们的气质和边界。

Nacos(Naming and Configuration Service)是阿里巴巴2018年开源的项目,它的野心从一开始就不只是配置中心——它想成为微服务的“一站式”基础设施,同时提供服务发现和配置管理两大核心功能。

这意味着,如果你用了Nacos,你可能就无需再引入Eureka或Consul了。

它的口号是“一个更易于构建云原生应用的动态服务发现、配置和管理平台”。

Apollo(阿波罗)则来自携程,2016年开源。

它从一开始就只专注做配置管理这一件事,并在这个领域深耕细作。

携程作为一家大型在线旅游平台,内部有着极其复杂的配置治理需求(多环境、多集群、权限控制、灰度发布等),Apollo正是为了解决这些企业级痛点而生的。

一个简单的比喻:Nacos像一个功能丰富的“瑞士军刀”,既能切菜(配置)又能开瓶(服务发现);Apollo则像一把顶级的“德国双立人厨师刀”,它只切菜,但在这个领域做到了极致。

02 谁更容易上手?

对于“好不好用”,最直观的感受就是上手成本。

我们先分别跑一个简单的Demo,看看两者的启动和集成难度。

Nacos:极简启动

# 下载最新版Nacos Server
wget https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.2.3.zip
unzip nacos-server-2.2.3.zip
cd nacos/bin

# 单机模式启动(Linux/Mac)
sh startup.sh -m standalone
# Windows
startup.cmd -m standalone

启动成功后,访问 http://localhost:8848/nacos,默认用户名密码都是 nacos。

然后创建一个Spring Boot项目,引入依赖:

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

在 bootstrap.properties 中配置:

spring.application.name=my-service
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

然后就可以在代码中使用了:

@RestController
@RefreshScope// 配置自动刷新注解
publicclass DemoController {

@Value("${user.name:default}")
private String userName;

@GetMapping("/config")
public String getConfig() {
return"Current user name: " + userName;
}

// 服务发现客户端注入
@Autowired
private DiscoveryClient discoveryClient;

@GetMapping("/services")
public List<String> services() {
return discoveryClient.getServices();
}
}

从下载到跑通第一个配置读取,熟练的话不超过10分钟。

Nacos的一体化设计让配置和发现共用同一个服务端,客户端配置也非常简洁。

Apollo:稍显复杂的部署

Apollo的部署稍微复杂一些,因为它本身就是一套分布式系统。它包含四个核心组件:

  • ConfigService:提供配置读取和推送接口
  • AdminService:提供配置管理接口
  • Portal:Web管理界面
  • MetaServer:服务发现(实际上是Eureka)

官方提供了Quick Start脚本,但依然需要几步操作:

# 下载Quick Start包
wget https://github.com/apolloconfig/apollo-quick-start/releases/download/v2.1.0/apollo-quick-start-2.1.0.zip
unzip apollo-quick-start-2.1.0.zip
cd apollo-quick-start
# 执行脚本(会自动启动MySQL和所有服务)
./demo.sh start

启动成功后,访问 http://localhost:8070,默认用户名/密码:apollo/admin。

Spring Boot集成:

<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>2.1.0</version>
</dependency>

在 application.properties 中配置:

app.id=my-service
apollo.meta=http://localhost:8080
apollo.bootstrap.enabled=true
apollo.bootstrap.namespaces=application

代码中使用:

@Component
publicclass ApolloConfigBean {

// 使用@Value注解,需要添加@EnableApolloConfig注解
@Value("${user.name:default}")
private String userName;

// 也可以直接使用API获取
public String getConfig(String key) {
Config config = ConfigService.getAppConfig();
return config.getProperty(key, "default");
}

// 监听配置变化
@ApolloConfigChangeListener
private void onChange(ConfigChangeEvent changeEvent) {
for (String key : changeEvent.changedKeys()) {
System.out.println(key + " changed!");
}
}
}

小结:在“开箱即用”这个维度上,Nacos完胜。它的单机模式启动简单,客户端配置也少。Apollo的Quick Start虽然也提供了脚本,但因为组件多,启动稍慢,而且会占用更多端口。对于只想快速尝鲜的开发者来说,Nacos更友好。

03 核心功能

“好用”不只是启动快,更重要的是在复杂场景下能不能解决问题。

我们逐一对比关键功能点。

3.1 配置管理基础能力

配置格式支持:两者都支持properties、yaml、xml、json等常见格式。Nacos的控制台支持直接编辑yaml,并提供了语法高亮;Apollo也支持文本编辑,但默认按key-value展示,对于yaml来说体验稍差。

版本管理:两者都支持配置的历史版本和回滚。Apollo的版本管理粒度更细,可以精确到每次发布,并支持对比、查看变更详情。Nacos也有版本管理,但相对简单。

环境与集群管理:Apollo天生支持多环境(DEV、FAT、UAT、PRO)和多集群(数据中心),每个环境的配置完全隔离,这对于企业级应用非常友好。Nacos通过命名空间(Namespace)和分组(Group)来实现隔离,理论上可以模拟出环境,但需要自己规划命名策略,没有Apollo那么直观。

举个例子,在Apollo中,创建一个DEV环境的配置和一个PRO环境的配置,完全是两个独立的配置项,互不影响。而在Nacos中,你可能需要在不同的Namespace中分别创建。

3.2 配置推送的实时性

Nacos的长轮询(Long Polling)机制

Nacos采用HTTP长轮询实现配置推送。我来带你看看源码级别的实现:

// Nacos客户端发起长轮询的核心逻辑
public void start() {
executor.scheduleWithFixedDelay(new LongPollingRunnable(), 0, retryInterval, TimeUnit.SECONDS);
}

class LongPollingRunnable implements Runnable {
@Override
public void run() {
// 构造订阅串,包含当前配置的md5值
String listeningConfigs = buildListeningConfigs();

Request request = new Request.Builder()
.url(serverAddr + "/v1/cs/configs/listener")
.post(body)
.build();

// 这里会阻塞最多30秒
try (Response resp = httpClient.newCall(request).execute()) {
if (resp.isSuccessful()) {
String content = resp.body().string();
// 如果有变更,立即拉取最新配置
if (StringUtils.isNotEmpty(content)) {
pullNewConfig(content);
}
}
}
}
}

服务端的处理更精妙:

  1. 客户端请求到达后,服务端先比较md5,如果有变更,立即返回
  2. 如果没有变更,使用Servlet 3.0的AsyncContext挂起请求(默认29.5秒)
  3. 同时启动一个定时任务,超时后返回空响应
  4. 如果期间配置变更,触发LocalDataChangeEvent事件,从挂起队列中找出相关客户端,立即返回
// 服务端挂起请求的核心代码
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(0); // 禁用容器默认超时

ClientSubscription sub = new ClientSubscription(keys, asyncContext);
// 加到订阅列表
subscribers.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(sub);

// 定时29.5秒后超时返回
ScheduledFuture<?> timeoutTask = scheduler.schedule(() -> {
asyncContext.getResponse().getWriter().write("");
asyncContext.complete();
}, 29500, TimeUnit.MILLISECONDS);

这种设计的精妙之处在于:既实现了准实时推送(秒级),又避免了WebSocket长连接的资源消耗,同时兼容所有HTTP中间件。

Apollo的定时轮询机制

Apollo客户端默认每60秒拉取一次最新配置。虽然可以通过apollo.refreshInterval调整,但本质上还是轮询。源码层面,Apollo使用Spring的DeferredResult实现异步化:

// Apollo服务端关键类:NotificationControllerV2
// 使用DeferredResult实现异步响应
@RequestMapping(method = RequestMethod.GET)
public DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> pollNotification(
@RequestParam(value = "appId") String appId,
@RequestParam(value = "cluster") String cluster,
@RequestParam(value = "notifications") String notificationsAsString,
@RequestParam(value = "dataCenter", required = false) String dataCenter,
@RequestParam(value = "ip", required = false) String clientIp) {

DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> deferredResult =
new DeferredResult<>(timeout);

// 将deferredResult存入队列,等待配置变更时唤醒
deferredResults.add(deferredResult);

return deferredResult;
}

两种机制的对比:

维度 Nacos长轮询 Apollo定时轮询
推送延迟 秒级(1-2秒) 依赖轮询间隔,默认60秒
服务端压力 中等(需维护挂起队列) 较低(无状态)
连接数 与客户端数量正相关 与轮询频率相关
穿透性 好(纯HTTP) 好(纯HTTP)

如果业务对配置变更实时性要求高(如秒杀开关、黑白名单),Nacos的长轮询更有优势;如果能接受分钟级延迟,Apollo也能满足大多数场景。

3.3 灰度发布与权限控制

这是Apollo的绝对强项,也是很多大厂选择它的核心原因。

Apollo的灰度发布:支持按IP、按机器、按用户标签进行配置的灰度发布。你可以先发布给一台机器验证,没问题再逐步推送到全量。同时支持配置的全量回滚和灰度回滚,操作界面非常友好。

Apollo的权限控制:提供应用级、环境级、命名空间级的多维度权限管理。你可以指定某个用户只能修改DEV环境的配置,不能碰PRO;或者只允许运维人员执行发布操作。所有的操作都有审计日志,谁在什么时间改了哪个配置,一目了然。

相比之下,Nacos的权限控制比较基础,虽然也支持RBAC,但粒度较粗。灰度发布需要自己通过多Namespace或多Group模拟,或者结合配置中心的元数据功能手动实现,对于大多数团队来说门槛较高。

3.4 服务发现功能

这是Nacos的独有优势,Apollo完全不支持服务发现。

Nacos的服务发现功能非常强大,支持健康检查、临时/持久实例、权重路由、监听查询等,完全可以替代Eureka或Consul。如果你的项目既需要配置中心,又需要服务发现,使用Nacos可以少维护一套系统,架构更简洁。

// Nacos服务发现客户端使用示例
@RestController
publicclass ConsumerController {

@Autowired
private LoadBalancerClient loadBalancerClient;

@Autowired
private RestTemplate restTemplate;

@GetMapping("/call")
public String call() {
// 从Nacos获取服务实例
ServiceInstance instance = loadBalancerClient.choose("provider-service");
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/hello";
return restTemplate.getForObject(url, String.class);
}
}

如果选择Apollo,你需要额外引入一套服务发现组件,比如Eureka、Nacos Discovery或Consul,增加了系统的复杂度。

04 数据模型对比

很多小伙伴在工作中分不清两者的数据模型,特别是“Namespace”这个概念,两者含义完全不同。

4.1 Nacos的三层模型

Nacos采用 Namespace + Group + DataId 三层结构:

  • Namespace:代表环境隔离,如dev、test、prod。客户端通过指定Namespace来访问不同环境的配置。
  • Group:代表逻辑分组,如DEFAULT_GROUP、DATABASE_GROUP。
  • DataId:代表具体的配置文件,通常格式为 ${serviceName}.properties。
# Nacos配置示例
spring.cloud.nacos.config.namespace=dev # 开发环境
spring.cloud.nacos.config.group=DEFAULT_GROUP # 默认分组
spring.cloud.nacos.config.dataId=order-service.properties

这种模型的好处是结构清晰,与Kubernetes的命名空间概念类似,理解成本低。但坏处是,如果你需要管理多个环境,必须在每个环境部署不同的Nacos集群,或者通过权限隔离来控制访问。

4.2 Apollo的四层模型

Apollo的数据模型更复杂:Environment + AppId + Cluster + Namespace。

  • Environment:环境,如DEV、FAT、UAT、PRO。
  • AppId:应用唯一标识。
  • Cluster:集群,可用于区分数据中心或逻辑分组。
  • Namespace:在这里Namespace是配置文件的概念,分为私有(private)和公共(public)两种。
# Apollo配置示例
app.id=order-service
apollo.meta=http://config-server:8080
apollo.bootstrap.namespaces=application,TEST1.public
apollo.cluster=default

Apollo的模型粒度更细,天然支持多环境、多集群,适合复杂的企业部署架构。

但缺点也很明显——配置项过多,初次上手容易迷失。

05 架构对比

我们用两张图来直观对比两者的架构。

5.1 Nacos架构

Nacos集群中各节点是对等的,通过共享存储(MySQL)来保证数据一致性。

客户端可以配置多个服务端地址,或者通过负载均衡器接入。架构相对简单。

5.2 Apollo架构

Apollo的架构组件较多,职责分离清晰:

  • ConfigService:负责配置的读取和推送
  • AdminService:负责配置的修改和发布
  • Portal:Web管理界面
  • MetaServer:基于Eureka的服务发现,帮助客户端找到ConfigService

这种架构的优点是每个组件都可以独立扩展,比如ConfigService可以水平扩展,而AdminService可以单独隔离。

缺点是部署和维护成本高,需要同时管理多个组件和数据库。

06 性能与扩展性

在性能方面,两者都能满足绝大多数场景。

我们曾做过压测,单台Nacos Server在8核16G配置下,可以稳定支撑5000+客户端的长轮询连接。

Apollo的ConfigService也是无状态的,扩展性很好。

但Apollo的一个潜在瓶颈是数据库。

因为所有的配置拉取都会查询数据库(虽然有缓存),当客户端数量非常大时,数据库压力会上升。

Nacos同样依赖数据库,但它在服务端有一层缓存,可以减少数据库查询。

在数据一致性方面,两者都依赖于数据库事务,保证了配置发布的原子性。

07 运维成本与社区生态

部署运维:Nacos明显更简单。单机模式一条命令,集群模式也只需要配置数据库和几个节点。Apollo最少需要启动三个组件(ConfigService、AdminService、Portal),且需要两个数据库(ConfigDB和PortalDB),对运维人员的要求更高。

监控告警:Nacos自带了简易的监控页面,可以查看集群状态、健康检查等。Apollo也有监控,但需要整合到企业监控系统。

社区生态:

  • Nacos:背靠阿里云,社区非常活跃,几乎每个月都有新版本,GitHub Star已超过27k。文档齐全,有中文社区。与Spring Cloud Alibaba、Dubbo等框架集成良好。
  • Apollo:携程开源,社区也很成熟,Star数约28k,但发布频率相对较低(约半年一个大版本)。文档详细,有企业微信交流群。

08 如何选择?

结合以上对比,我给出一些选型建议:

推荐选择 Nacos 的场景

  1. 新项目,技术栈为Spring Cloud Alibaba:天然集成,配置+发现一体,开发效率高。
  2. 中小团队,运维人力有限:部署简单,维护成本低。
  3. 需要服务发现功能:用Nacos可以省掉一套Eureka或Consul。
  4. 对配置变更实时性要求较高:长轮询机制秒级生效。

推荐选择 Apollo 的场景

  1. 大型企业,有复杂的配置治理需求:多环境、多集群、严格的权限控制和灰度发布。
  2. 配置中心需要与现有CMDB、监控系统深度集成:Apollo的开放API和丰富事件机制更适合。
  3. 对配置变更的安全性要求极高(金融、政务):完整的审计日志和细粒度权限控制。
  4. 团队已经有一套成熟的服务发现方案(如自研或Consul),只需要配置中心。

折中方案

也有一些团队采用混合架构:服务发现用Nacos(因为它轻量),配置管理用Apollo(因为它专业)。

这种方案虽然增加了组件,但可以发挥各自优势,适合大型项目。

09 如何实现配置动态刷新?

最后,给出一个示例,展示在两种框架下如何实现配置的动态刷新。

Nacos 动态刷新

@RestController
@RefreshScope
public class NacosConfigController {

@Value("${my.config:default}")
private String myConfig;

@GetMapping("/nacos/config")
public String getConfig() {
return "Current config: " + myConfig;
}
}

在Nacos控制台修改配置后,无需重启,调用接口就会看到新值,@RefreshScope是关键。

Apollo 动态刷新

@RestController
@RefreshScope
public class ApolloConfigController {

@Value("${my.config:default}")
private String myConfig;

@GetMapping("/apollo/config")
public String getConfig() {
return "Current config: " + myConfig;
}
}

看起来几乎一样,但需要注意两点:

  1. 需要@EnableApolloConfig注解(通常在启动类或配置类上加)。
  2. Apollo默认的刷新时机是60秒轮询,如果希望立即生效,可以调用ConfigService的API强制刷新,或集成消息总线。

总结

Nacos和Apollo没有绝对的“更好”,只有“更适合”。

如果让我用一个词形容它们,我会说:

  • Nacos:轻盈、全能、云原生
  • Apollo:专业、严谨、企业级

有些小伙伴可能会说:“我们团队小,用哪个都行。”

确实,对于很多初创项目,两者都能满足需求。

但作为架构师,我们需要考虑未来3-5年的发展:业务会变复杂,团队会扩大,配置治理的挑战也会随之而来。选型时多思考一步,未来会少踩很多坑。

回到最初的问题:Nacos和Apollo,哪个更好用?

我的答案是:

  • 如果你追求简单高效、配置和服务发现一体化,选Nacos,它真的很好用。
  • 如果你身处大型组织,需要精细化的配置治理和安全性,选Apollo,它在复杂场景下的好用程度远超你的想象。
文章目录
  1. 1. 前言
  2. 2. 01 设计理念
  3. 3. 02 谁更容易上手?
    1. 3.0.1. Nacos:极简启动
    2. 3.0.2. Apollo:稍显复杂的部署
  • 4. 03 核心功能
    1. 4.0.1. 3.1 配置管理基础能力
    2. 4.0.2. 3.2 配置推送的实时性
      1. 4.0.2.1. Nacos的长轮询(Long Polling)机制
      2. 4.0.2.2. Apollo的定时轮询机制
    3. 4.0.3. 3.3 灰度发布与权限控制
    4. 4.0.4. 3.4 服务发现功能
  • 5. 04 数据模型对比
    1. 5.0.1. 4.1 Nacos的三层模型
    2. 5.0.2. 4.2 Apollo的四层模型
  • 6. 05 架构对比
    1. 6.0.1. 5.1 Nacos架构
    2. 6.0.2. 5.2 Apollo架构
  • 7. 06 性能与扩展性
  • 8. 07 运维成本与社区生态
  • 9. 08 如何选择?
    1. 9.0.1. 推荐选择 Nacos 的场景
    2. 9.0.2. 推荐选择 Apollo 的场景
    3. 9.0.3. 折中方案
  • 10. 09 如何实现配置动态刷新?
    1. 10.0.1. Nacos 动态刷新
    2. 10.0.2. Apollo 动态刷新
  • 11. 总结