注册中心到底解决了什么问题
单体应用里,A 调 B 就是一次方法调用;拆成微服务后,A 需要知道 B 的地址,而 B 的地址会随扩缩容、发版、故障漂移不断变化。注册中心负责三件事:服务启动时登记自己,调用方按服务名拉取可用实例,实例下线或心跳超时后被摘除。Nacos 把这套能力和配置中心放在同一个进程里,所以 2025 年的 Spring Cloud Alibaba 项目仍然大量用它。
但“注册上了”和“能调通”是两件事。很多人第一次接 Nacos,控制台里服务列表明明是绿的,Feign 调用却报 No instances available for order-service,或者请求打到一个已经停掉的 Pod 上。下面按接入顺序讲清楚,再集中说坑。
版本和端口先对齐
| 组件 | 本文示例 | 说明 |
|---|---|---|
| JDK | 21 | LTS,Spring Boot 3.x 的基线 |
| Spring Boot | 3.3.x | 3.4 也可,但 Cloud 版本要配套 |
| Spring Cloud | 2023.0.x | 与 Boot 3.3 对应 |
| Spring Cloud Alibaba | 2023.0.3.x | 引入 Nacos 客户端 |
| Nacos Server | 2.4.x | 服务端独立部署 |
需要特别记住:Nacos 2.x 不是只开 8848。客户端通过 gRPC 长连接通信,还需要 9848(客户端请求服务端)和 9849(服务端间同步)。只映射 8848 时,控制台能打开、服务也可能注册成功,但心跳和推送会超时,表现为实例反复上下线。容器部署时三个端口都要暴露,或者干脆用 host 网络。
最小可用配置
引入依赖后,spring.application.name 就是服务名,不需要再写 @EnableDiscoveryClient:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
server:
port: 8081
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev-id
group: ORDER_GROUP
username: nacos
password: ${NACOS_PASSWORD}
namespace 要填命名空间的 ID,不是显示名称;group 要和消费方一致,默认都是 DEFAULT_GROUP。这两项只要有一项对不上,控制台看到的就是两个互不相干的服务。密码不要硬编码,走环境变量或密钥管理。
临时实例、心跳与保护阈值
默认注册的是临时实例(ephemeral: true):客户端每 5 秒发一次心跳,15 秒没心跳标记不健康,30 秒后摘除。这套机制适合无状态的 Spring Boot 应用。如果服务是数据库、旧系统这类“进程在但心跳机制不适用”的场景,可以注册为持久实例,由服务端主动探测。
还有一个容易被忽略的开关是保护阈值。当健康实例占比低于阈值时,Nacos 会把全部实例(包括不健康的)返回给调用方,避免流量瞬间归零。这个设计本意是保可用性,但调用方如果没配重试,就可能把请求打到坏实例上。所以保护阈值要结合客户端的重试和超时一起看,不能只调注册中心。
常见坑与排查顺序
坑一:namespace 填了名字。 控制台显示 dev,配置里写 namespace: dev,实际 ID 是一串 UUID,结果服务注册到了 public。以控制台命名空间详情里的 ID 为准。
坑二:Docker 里注册了容器 IP。 容器使用 bridge 网络时,Nacos 客户端默认取容器网卡地址,宿主机和其他机器根本路由不到。可以显式指定 spring.cloud.nacos.discovery.ip,或选择正确的 network-interface,生产上更推荐让 Pod 使用可路由的 CNI 地址。
坑三:服务名带下划线或大小写混用。 Nacos 对服务名敏感,order_service 和 order-service 是两个服务。团队内最好用 Maven 的 artifactId 统一生成,不要手写。
坑四:只改了一半环境。 注册中心和配置中心的 namespace、group、账号密码是分开配置的,改了一个忘了另一个,启动时配置拉不到,服务却还能注册。
真出问题时按这个顺序查:先看 Nacos 控制台的服务列表和实例数,确认注册成功;再调 /nacos/v1/ns/instance/list?serviceName=order-service 看接口返回;然后看客户端 logs/nacos/naming.log 有没有心跳失败或连接超时;最后在调用方打日志确认拿到的实例列表。多数“找不到服务”的问题,最后都落在 namespace/group 不一致、9848 端口不通、或者客户端缓存了旧列表这三类上。
评论