主题
注册发现与配置中心:Nacos 双模一体化
本文是微服务架构系统学习系列的 L2 核心篇。前置:27. 从单体到微服务:演进与拆分原则。 学完可以配合面试题食用:02-service-discovery-registry-cap-nacos-consul-eureka-zk、06-config-center-nacos-apollo-spring-cloud-config、08-配置中心-推拉模式一致性保证
服务发现三要素:注册、心跳与查询
微服务拆开后,每个服务的 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 的分水岭
注册中心不是数据库,选型时不能只看一致性强弱,得看服务发现场景的真实需求。
| 特性 | Zookeeper | Eureka | Nacos |
|---|---|---|---|
| 一致性模型 | 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 协议说明