文章目录
- 一、Nacos安装部署
- 二、Nacos服务注册与发现
- 三、Nacos负载均衡
- 四、Nacos健康检查机制
- 五、Namespace命名空间实现环境隔离
- 六、Nacos配置中心
-
- 为什么要用配置中心
- [快速上手Nacos Config](#快速上手Nacos Config)
- DataId完整拼接规则
- [七、Nacos vs Eureka对比](#七、Nacos vs Eureka对比)
- 使用过程中遇到的问题及解决方案
-
- [1. 端口:order-serivce配了 8080,为什么看到一个 9848 的连接](#1. 端口:order-serivce配了 8080,为什么看到一个 9848 的连接)
- [2. 数据库:`Public Key Retrieval is not allowed`](#2. 数据库:
Public Key Retrieval is not allowed) - [3. 服务调用: `No instances available for 121.41.65.106`](#3. 服务调用:
No instances available for 121.41.65.106) - [4. Nacos 服务端:改权重报 `Raft Group naming_instance_metadata did not find the Leader node`](#4. Nacos 服务端:改权重报
Raft Group [naming_instance_metadata] did not find the Leader node)
在微服务技术栈中,注册中心是整个微服务体系的核心组件。早年很多项目使用Eureka做服务注册发现,但是 Eureka 2.0宣布闭源之后,阿里开源的 Nacos 迅速成为国内微服务首选。 Nacos全称 Dynamic Naming and Configuration Service,它不只是注册中心,同时还提供配置管理、动态DNS服务,一套组件搞定服务发现+配置中心两大能力,支持Java、Go、Python等几乎主流开发语言。
一、Nacos安装部署
Linux单机安装
#解压
unzip nacos-server-2.2.3.zip
#单机启动
bash startup.sh -m standalone
端口放行: Nacos除主端口8848,还需要开放 9848、9849(端口+1000、+1001),否则客户端通信异常。
访问地址:http://服务器IP:8848/nacos
二、Nacos服务注册与发现
Nacos实现SpringCloud标准服务注册发现,和Eureka编程模型很像,区别是不需要自己搭建注册服务,只需要启动Nacos服务端,微服务引入对应依赖即可。
1、Maven依赖
父工程统一管理版本
xml
<properties>
<spring-cloud-alibaba.version>2025.1.0.0</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
微服务模块引入:
xml
<!--nacos注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--springcloud loadbalancer负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
2、application.yml配置
yml
spring:
application:
name: product-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
3、远程调用RestTemplate
java
@Configuration
public class BeanConfig {
@Bean
@LoadBalanced //开启负载均衡,支持服务名调用
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
调用时直接写服务名称,不再写IP端口
java
//直接使用服务名 product-service
String url = "http://product-service/product/"+orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
常见报错
java.net.UnknownHostException: product‑service:大概率缺少spring‑cloud‑loadbalancer依赖。
启动多个实例,Nacos控制台服务列表就可以看到多个服务节点,自动实现负载均衡。
三、Nacos负载均衡
SpringCloud LoadBalancer默认轮询策略,Nacos提供权重、集群优先等高级流量控制,需要手动开启Nacos负载均衡策略。
yaml
spring:
cloud:
loadbalancer:
nacos:
enabled: true
1、服务上下线
Nacos控制台 → 服务详情,可以手动对实例执行下线操作。下线节点不会接收流量,上线之后恢复接收请求。适合机器维护、版本灰度场景。

2、权重配置
每个实例默认权重=1;权重数值越大分到流量越多。 控制台找到实例点击编辑,修改权重。比如设置权重0.1,该实例只会分到少量流量。
注意: 修改权重报错 Raft Group did not find the Leader node
- 原因:Raft选主异常
- 解决:删除nacos目录
data/protocol文件夹,重启nacos。
3、同集群优先访问
实际生产,服务部署在北京、上海多个机房。希望优先调用本机房服务实例,跨机房网络延迟高,只在本机实例全部不可用时访问其他机房。
配置product-service服务所属集群名称:
java
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
cluster-name: SH #上海机房
另外两个个实例VM参数指定BJ集群: ‑Dserver.port=9091 ‑Dspring.cloud.nacos.discovery.cluster‑name=BJ
order-service同样配置cluster‑name:SH,开启nacos负载均衡,调用优先访问SH集群实例;SH实例全部下线,才会转发BJ集群。
四、Nacos健康检查机制
Nacos实例分为临时实例、非临时(永久)实例,两种实例健康检查机制完全不一样。
| 实例类型 | 检查方式 | 操作 |
|---|---|---|
| 临时实例(默认) | 客户端心跳上报 | 客户端每5秒上报心跳;15秒没心跳标记不健康;30秒删除实例 |
| 非临时实例 | 服务端反向探测 | Nacos服务端主动探测实例;宕机不会自动删除,标记不健康 |
配置非临时实例:
yaml
spring:
cloud:
nacos:
discovery:
ephemeral: false #关闭临时实例,永久实例
注意:同一个IP端口服务,不能切换临时/非临时,直接启动报错。需要删除nacos/data/protocol/raft,重新启动nacos。
五、Namespace命名空间实现环境隔离
企业开发中一个服务通常有四种环境:
- 开发环境
- 测试环境
- 预发布环境
- 发布环境
其中预发布环境和发布环境都属于正式环境,数据库,配置都是一样的,预发布环境不对外,发布环境对外。
开发、测试、生产环境需要隔离,不同环境服务之间不能互相调用。Nacos使用namespace命名空间实现隔离。 默认命名空间为public。不同namespace之间服务完全不可见。
Nacos控制台新建命名空间,拿到命名空间ID。 微服务配置指定namespace:

yaml
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: "命名空间ID"
注意:服务注册的namespace 和配置中心namespace是两套独立配置,互不影响
六、Nacos配置中心
为什么要用配置中心
传统把配置写在项目yml文件:
- 修改配置必须重启服务;成百上千实例挨个重启效率极低
- 多人开发配置文件容易冲突
Nacos配置中心把配置统一托管,配置修改实时推送,服务无需重启即可热更新配置。
快速上手Nacos Config
- 引入依赖
xml
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--SpringCloud2020之后bootstrap默认关闭,手动引入-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
application.yml:
product-service/../application.yml
yaml
spring:
config:
import: nacos:product-service
cloud:
nacos:
discovery:
server-addr: 121.41.65.106:8848
config:
server-addr: 121.41.65.106:8848
order-service/../application.yml
yaml
spring:
config:
import: nacos:order-service
cloud:
nacos:
config:
server-addr: 121.41.65.106:8848
discovery:
server-addr: 121.41.65.106:8848 # Nacos服务器地址
loadbalancer:
nacos:
enabled: true
-
Nacos控制台新建配置 DataID:
product‑service,与应用名称保持一致;格式properties,写入配置nacos.test.num=5
-
代码读取配置,实现热更新
java
@RestController
@RequestMapping("/config")
@RefreshScope //核心注解,配置变更自动刷新bean
public class NacosController {
@Value("${nacos.test.num:0}")
private Integer nacosNum;
@RequestMapping("/get")
public Integer get(){
return nacosNum;
}
}
访问接口,修改控制台配置内容,无需重启服务,接口返回值动态变化。
DataId完整拼接规则
java
${prefix}-${spring.profiles.active}.${file‑extension}
- prefix 默认等于
spring.application.name - spring.profiles.active:环境(dev/test/prod)
- file‑extension 文件格式,默认properties
服务启动会自动加载3份配置,优先级从高到低: product‑service‑dev.properties > product‑service.properties > product‑service 高优先级配置覆盖低优先级配置。
常见报错
No spring.config.import property has been defined:缺少spring‑cloud‑starter‑bootstrap依赖。- 读取不到配置:核对namespace、DataId、文件格式是否匹配。
七、Nacos vs Eureka对比
- 相同点:都可以实现服务注册、服务发现。
- 不同点:
| 对比维度 | Eureka | Nacos |
|---|---|---|
| 功能 | 仅服务注册发现 | 注册中心+配置中心+动态DNS,能力丰富 |
| CAP | AP | 支持切换AP/CP;临时实例AP,永久实例CP,可以混合 |
| 数据同步 | 客户端拉取模式,默认30s拉取一次 | 推送模式,服务实例变化实时推送客户端 |
使用过程中遇到的问题及解决方案
1. 端口:order-serivce配了 8080,为什么看到一个 9848 的连接
排查:抓端口状态看到两行关键信息------
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 22692
TCP 10.14.24.97:65130 121.41.65.106:9848 ESTABLISHED 22692
第一行是 LISTENING,说明 8080 确实在监听,配置生效了。
第二行是 ESTABLISHED,方向是"本机 → 远端",是主动连出去的,不是被访问。
原因:Nacos 2.x 之后,一个 8848 其实对应三个端口,客户端会用主端口自动推导:
- 8848 = HTTP 端口,控制台和 OpenAPI(8848 + 0)
- 9848 = 客户端 gRPC 端口(8848 + 1000),注册、心跳、订阅全走它
- 9849 = 服务端之间的 gRPC 同步端口(8848 + 1001)
- 7848 = Nacos 1.x 遗留的 Jraft 端口
所以只要应用是 Nacos 客户端,启动后就必然会往 9848 建一条长连接。
结论 :server.port 管的是"别人怎么访问我",9848 管的是"我怎么连 Nacos",两者完全无关,不需要改任何东西,访问服务就用 http://localhost:8080/...。
衍生提醒 :如果 Nacos 2.x 注册失败报 Client not connected, current status: STARTING,大概率是服务端防火墙只放行了 8848,没放 9848/9849。云服务器要放行 8848、9848、9849(Nacos 1.x 还要加 7848)。
2. 数据库:Public Key Retrieval is not allowed
报错链条:
MyBatisSystemException
→ CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
→ com.mysql.cj.exceptions.UnableToConnectException: Public Key Retrieval is not allowed
→ CachingSha2PasswordPlugin.nextAuthenticationStep
原因 :MySQL 8.0 起,root 用户的默认认证插件从 mysql_native_password 换成了 caching_sha2_password。
- 密码在服务端缓存命中时走"快速认证",直接通过;
- 缓存未命中时(首次连接、MySQL 重启后、
FLUSH PRIVILEGES之后)需要完整认证,客户端必须用服务端的 RSA 公钥加密密码; - 公钥要么走 SSL 加密通道下发,要么让客户端主动索取,而后者被 Connector/J 默认禁止(怕被中间人利用爆破密码);
- 连接串里写了
useSSL=false,把 SSL 那条路也堵死了。
两条路都不通,于是在握手阶段就失败。注意报错时还没执行 SQL,纯粹是连不上库。
解决 :连接串补参数allowPublicKeyRetrieval=true,允许客户端在握手时向 MySQL 服务器索要RSA 公钥。
yaml
url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
另外一种解决方案 :改用 SSL 连接 useSSL=true&requireSSL=true。走 SSL 时不需要 allowPublicKeyRetrieval,二者选一,生产环境更推荐。
安全提醒 :allowPublicKeyRetrieval=true 在非加密连接下理论上有被伪造公钥、离线爆破的风险。本地 127.0.0.1 无所谓,公网数据库请走 SSL。
3. 服务调用: No instances available for 121.41.65.106
报错:
WARN RoundRobinLoadBalancer : No servers available for service: 121.41.65.106
ERROR IllegalStateException: No instances available for 121.41.65.106
报错里把 IP 当成了服务名。
错误代码:
java
String url = "http://121.41.65.106:8848/product/" + orderInfo.getProductId();
原因 :RestTemplate 上打了 @LoadBalanced,这个注解会塞进一个 LoadBalancerInterceptor,它把 URL 的 host 段当作 serviceId,端口直接丢弃,然后去服务发现里查这个"服务名"的实例列表。
于是 121.41.65.106:8848 被解析成 serviceId = 121.41.65.106,去 Nacos 查当然查不到。
更根本的错误是:121.41.65.106:8848 是 Nacos 服务端自己的地址,不是 product-service 的地址。
正确写法:
java
// 服务名必须与 product-service 在 Nacos 中注册的 spring.application.name 一致
String url = "http://product-service/product/" + orderInfo.getProductId();
product-service 正是它 application.yml 里 spring.application.name 的值,也是 Nacos 注册中心里的服务名。LoadBalancer 会自动把它替换成真实地址 192.168.86.1:9090。
4. Nacos 服务端:改权重报 Raft Group [naming_instance_metadata] did not find the Leader node
报错:
errCode: 500, errMsg: do metadata operation failed
caused: ConsistencyException: The Raft Group [naming_instance_metadata] did not find the Leader node
这不是客户端问题,是 Nacos 服务端自己的 Raft 一致性组坏了。
很多人以为"临时实例走 Distro 协议、存在内存里,跟 Raft 无关"------在 Nacos 2.2 里这个认知是错的 。Nacos 2.2 引入了实例元数据持久化,实例的权重、metadata、enabled 这些元数据的变更,被统一交给独立的 Raft 组 naming_instance_metadata 做一致性持久化,不分临时实例还是持久实例。
所以"改权重"在服务端等价于"写一条元数据 Raft 日志",Raft 组没有 Leader,就写不进去。
排查:查服务端状态接口确认版本和模式------
bash
curl "http://<nacos>:8848/nacos/v1/console/server/state"
# {"version":"2.2.3","standalone_mode":"standalone",...}
单机模式下 Raft 本该自己选自己当 Leader,现在选不出来,说明该节点的 Raft 数据或状态坏了。
常见成因:
- Raft 数据目录损坏或残留(最常见)------
data/protocol/raft下的日志、快照、meta文件半写损坏,多因kill -9、磁盘写满、异常断电 - 曾经用集群模式启动过------
meta里记录了旧 peer 列表,后来改成单机,残留的 peer 列表让节点找不到自己 - 机器 IP 变了------换了网卡或重启后 IP 变化,而 raft
meta里还记着老地址 cluster.conf或nacos.core.member.list残留已下线的节点地址- 系统时钟不同步,影响选举超时判定
解决方案 在 Nacos 服务器上执行:
bash
cd ~/tools/nacos
# 1. 停服务
bash bin/shutdown.sh
ps -ef | grep nacos | grep -v grep # 确认退干净
# 2. 备份
mv data/protocol data/protocol.bak.$(date +%F-%H%M)
# 3. 单机模式启动
bash bin/startup.sh -m standalone
# 4. 看 Raft 选举日志
tail -n 100 logs/alipay-jraft.log
注意 :只动 data/protocol,绝对不要删 data/derby-data。
data/protocol/= Raft 数据(实例元数据、持久化实例),删了会重建data/derby-data/时配置中心的配置内容,删了配置全没,且不可恢复
删完data/protocol :自定义权重回到默认值 1.0;持久化实例消失需重新注册;临时实例靠客户端心跳自动重新注册,基本无感。
确诊用的日志文件:
bash
grep -i -E "did not find|elect|PEER|Leader" logs/alipay-jraft.log | tail -50
grep -i -E "did not find|elect|Leader" logs/protocol-raft.log | tail -50
grep -i -E "did not find|elect|Leader" logs/naming-raft.log | tail -50
看到 PEER ... not in the peer list 或反复 start election 但选不上,印证了"旧 peer 列表残留 / IP 变了";看到 Corrupted、CRC、unexpected end of file 则是数据文件损坏。
建议: 启动方式固定成单机就别来回切集群/单机;别用 kill -9 停 Nacos,一定用 shutdown.sh 让 Raft 正常落盘。