Skip to content

注册发现与配置中心:Nacos 双模一体化

本文是微服务架构系统学习系列的 L2 核心篇。前置:27. 从单体到微服务:演进与拆分原则。 学完可以配合面试题食用:02-service-discovery-registry-cap-nacos-consul-eureka-zk06-config-center-nacos-apollo-spring-cloud-config08-配置中心-推拉模式一致性保证

服务发现三要素:注册、心跳与查询

微服务拆开后,每个服务的 IP 和端口都在动态变化(弹性伸缩、故障迁移、滚动部署)。服务 A 调用服务 B,不能把 B 的地址硬编码写在配置里——地址变了就得改配置重启,等于没拆。

服务发现解决的就是"你知道我叫什么,但不知道我在哪"的问题。三个基础动作:

  • 注册:服务启动时把自己的 IP+端口+服务名写进注册中心
  • 心跳:服务定期向注册中心报活,超时未报则标记为不健康
  • 查询:调用方去注册中心拿目标服务的可用地址列表

客户端发现 vs 服务端发现

关键区别在于负载均衡在哪做:

客户端发现                              服务端发现
┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│ Service A │---->│ Registry │     │ Service A │---->│    LB    │
│(内嵌LB逻辑)│     └──────────┘     │          │     │(独立组件) │
└──────────┘                         └──────────┘     └────┬─────┘
     │                                                      │
     ▼                                                      ▼
┌──────────┐                                           ┌──────────┐
│ Service B │<--- 返回地址列表,A自己选一个              │ Service B │
└──────────┘                                           └──────────┘

客户端发现:A 从注册中心拿到 B 的所有实例,自己用轮询/随机/LRU 选一个。直接、少一层,但每个语言都要实现一遍 LB 逻辑。

服务端发现:A 把请求发给 LB(如 Nginx),LB 从注册中心同步地址列表后再转发。LB 统一做,语言无关,但 LB 本身是单点(需要高可用)和性能瓶颈。

Spring Cloud 体系中,Ribbon/LoadBalancer 走客户端发现模式,Feign 调用时自动内嵌 LB。

注册中心选型对比:CP vs AP 的分水岭

注册中心不是数据库,选型时不能只看一致性强弱,得看服务发现场景的真实需求。

特性ZookeeperEurekaNacos
一致性模型CP(强一致)AP(最终一致)AP(Distro 协议)
健康检查长连接 session 过期心跳 30s + 续约心跳 + UDP/gRPC 推送
推送方式Watch 推客户端轮询拉临时节点 UDP+gRPC 推,持久节点轮询
服务数量承载~5000(写压力大)~10000+~10000+

Zookeeper 的强一致在服务发现场景下反而是缺点:写操作要过 Zab 协议多数派确认,Leader 挂掉时有几十秒不可用,期间所有服务都发现不了新地址。Eureka 的 AP 思路更务实——服务发现场景容忍短暂读到过期数据,但不能容忍不可用。Nacos 在这个基础上做了改进:用 Distro 协议(类似 Gossip 的最终一致)做 AP,同时支持临时/持久两种节点类型。

Nacos 的 Distro 协议简图

Client A ──注册──> Nacos Node 1

                      │  Distro 异步同步

              Nacos Node 2 ──> Nacos Node 3

                      │  查询:本地读 + 兜底查询

Client B ──查询──> Nacos Node 2

每个节点接收写请求后,异步同步给其他节点。查询时直接从本地内存返回,即使节点间数据有短暂延迟,也保证读请求不阻塞。这就是"可用性优先"的落地。

配置中心为什么需要独立出来

服务配置的变化频率远高于代码发布。改个数据库连接池大小、调个日志级别,犯不着重新打包部署。配置中心解决的问题:

  • 集中管理:不再散落在各服务的 .properties 里,git 也不一定是最新版本
  • 动态推送:改配置不重启,热生效
  • 灰度与回滚:先推给 10% 实例验证,出问题一键回滚

长轮询 vs 长连接推送

Nacos 配置中心使用长轮询(Long Polling)作为默认手段:

  • 客户端请求配置,服务端没有变化时,连接不立即返回,hold 住 30s
  • 30s 内如果有配置变更,立即返回新数据
  • 30s 超时后返回 304,客户端立即发起下一次轮询

这种做法的好处是兼容性好(HTTP 协议),且对服务端连接数压力比长连接小。缺点是有 30s 的理论延迟上限。Nacos 在 2.0 后也支持了 gRPC 双端流推送,对实时性要求高的场景(如 Sentinel 规则)可以走 gRPC 通道。

实践:Nacos namespace/group/dataId 三级隔离

Nacos 配置管理的三层结构:

namespace (环境隔离: dev/test/prod)
  └── group (业务分组: DEFAULT_GROUP / DB_GROUP / CACHE_GROUP)
       └── dataId (具体配置项: user-service-dev.properties)
  • namespace:多环境隔离的边界。不同 namespace 的配置完全不可见,天然防止误操作。生产环境单独一个 namespace,开发人员不应该有生产 namespace 的写权限。
  • group:同一环境内按逻辑分组。数据库相关配置放 DB_GROUP,缓存相关放 CACHE_GROUP,方便按场景批量推送。
  • dataId:一个配置条目。通常用 {应用名}-{profile}.{后缀} 命名,和应用内的 spring.application.name 匹配。

动手实操:Spring Cloud Alibaba 服务注册 + 动态配置

服务注册(Provider)

java
// 1. 引入依赖 pom.xml
// spring-cloud-starter-alibaba-nacos-discovery

// 2. application.yml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev

// 3. 启动类
@SpringBootApplication
@EnableDiscoveryClient  // 自动注册到 Nacos
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

// 4. 对外接口
@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public String getUser(@PathVariable Long id) {
        return "user-" + id;
    }
}

动态配置(Consumer 使用 @RefreshScope)

java
// 1. Nacos 控制台新建配置
// dataId: order-service-dev.yaml
// 配置内容:
//   order:
//     timeout: 3000
//     retry-count: 3

// 2. application.yml
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        namespace: dev
        group: DEFAULT_GROUP

// 3. 动态刷新 Bean
@Component
@RefreshScope  // 关键:配置变更时重新创建这个 Bean
@ConfigurationProperties(prefix = "order")
public class OrderConfig {
    private int timeout;
    private int retryCount;

    // getters & setters...
}

// 4. 使用
@RestController
public class OrderController {
    @Autowired
    private OrderConfig orderConfig;

    @GetMapping("/order/config")
    public String getConfig() {
        return "timeout=" + orderConfig.getTimeout()
            + ", retryCount=" + orderConfig.getRetryCount();
    }
}

Nacos 控制台操作步骤

1. 访问 http://localhost:8848/nacos,默认账号密码 nacos/nacos
2. 左侧菜单:命名空间 → 创建(dev / test / prod)
3. 配置管理 → 配置列表 → 选择命名空间 → 新建配置
4. 填入 dataId、group、配置格式(YAML/Properties)、配置内容
5. 发布后,配置即推送到所有订阅该 dataId 的客户端
6. 修改配置 → 发布 → 客户端自动刷新(@RefreshScope 生效)

常见误区与小结

  • 注册中心选 ZK 就是更好:ZK 强一致对服务发现来说是过剩能力,换来的是不可用窗口和写性能瓶颈。服务发现场景优先选 AP。
  • 配置中心改了不生效,重启服务才生效:检查有没有加 @RefreshScope,以及配置文件的 file-extension 是否和 Nacos 控制台一致。
  • namespace 和 group 都用来做环境隔离:namespace 才是环境隔离的正解。group 做环境隔离会跨 namespace 泄漏,而且 Nacos 控制台权限控制的最小粒度是 namespace。
  • 所有配置都放配置中心:只放需要动态变更的配置。不会变的内容(数据库驱动类名、日志框架固定参数)放本地配置文件,减少配置中心压力。
  • 配置变更不审计:Nacos 内置了变更历史,上线前配置变更应该走审批流程,不能直接在生产环境控制台改。

小结:注册发现和配置中心是微服务基础设施的起点。Nacos 一体化的方案把两个组件合二为一,减少了运维复杂度。理解它背后的 AP 选型和 Distro 协议,比记住 API 调用更重要。下一篇讲服务间通信,从 REST 到 gRPC,看协议选型如何影响性能。

参考

参考:Nacos 官方文档 https://nacos.io、Spring Cloud Alibaba 官方示例、Distro 协议说明

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。
粤ICP备2026104257号-1