跳到正文

Nacos 生产踩坑 ​

一句话:Nacos 的线上问题大多不是「注册中心 bug」,而是四类错配:实例的生命周期被当成永久、配置的坐标/刷新方式对不上、连接与容量被接入层打断、客户端与服务端版本各说各话。

适用版本:Nacos 3.2.4;涉及 2.x 的差异单独标注。命令中的 v3 路径以 3.x 为准;2.x 对应 v1/v2 路径,迁移前先确认 API 版本。

为什么需要 ​

Nacos 同时承载注册与配置,故障表现却经常「串台」:实例消失被当成网络问题,配置不生效被当成 Nacos bug,升级后 404 被当成服务没起来。真正的原因往往在四个边界上:

  • 生命周期:临时实例靠心跳和连接活着,不是注册一次就永久存在。
  • 坐标:namespace/group/dataId 任一错位,表现都是「没有数据」,不是「数据错了」。
  • 接入层:8848 通不代表 9848 通;LB 空闲超时会打断长连接。
  • 版本:客户端、服务端、Spring Cloud Alibaba 三者有兼容矩阵,混搭时症状千奇百怪。

核心机制 ​

先按症状分两条排查线,不要一上来就重启:

图里的分叉点是「先证明链路,再证明数据」:连接断了,查网络和容量;连接正常,才去查生命周期和配置坐标。顺序反了,就会在错误的层面翻日志。

坑一:临时实例被误删/心跳丢失 ​

  • 现象:实例注册后约 30s 从列表消失;或运行中周期性消失又出现。
  • 证据:控制台实例列表、instance/list 查询、客户端日志中的心跳/重连记录;服务端连接数是否突降。
  • 根因:ephemeral=true 的实例靠心跳续命。用 curl 注册后不发心跳、进程长时间 GC、网络抖动导致心跳丢失,都会触发 15s 不健康、30s 剔除。
  • 处置:长期实例用 SDK 注册;不能发心跳的用 ephemeral=false(接受 Raft 代价);排查 GC 停顿与网络;下线走注销而不是直接杀进程。

坑二:推空保护误配 ​

  • 现象:开启后服务缩容到 0,调用方仍打旧实例;或未开启时一次误删导致全线无实例。
  • 证据:namingPushEmptyProtection 配置、客户端缓存内容、服务端实例列表是否为空。
  • 根因:Java 客户端该参数默认 false;它只忽略「空列表推送」,不区分「故障空」和「合法缩零」。
  • 处置:先定义空列表语义;故障敏感的服务开启,合法缩零的服务不要开;开启后仍要监控「缓存实例数与服务端不一致」。

坑三:配置刷新不生效 ​

  • 现象:控制台显示新值,应用仍用旧值;重启后才生效。
  • 证据:/actuator/env 的最终值、启动日志中的 dataId/group、Refresh keys changed 日志、~/nacos/config/ 快照时间。
  • 根因:@Value 没配 @RefreshScope;@ConfigurationProperties 没被容器管理;static/final 字段;DataSource/线程池已建立;同名 key 被本地配置覆盖。
  • 处置:动态项改用 @ConfigurationProperties;@Value 加 @RefreshScope;连接池类走滚动重启;用 /actuator/env 确认优先级,不要凭印象。

坑四:Namespace 隔离导致读错配置 ​

  • 现象:测试环境读不到生产配置,或生产读到了测试的值;服务注册在 A,调用方在 B 找不到实例。
  • 证据:应用配置里的 namespace 值、控制台当前 Namespace、客户端启动日志中的 namespace ID。
  • 根因:Namespace 用 ID 不用名称;2.x 默认空字符串、3.x 默认 public;升级后旧配置可能落在另一个 Namespace。
  • 处置:统一用 Namespace ID;升级前核对迁移说明;在启动日志里打印实际 namespace/group/dataId;把「坐标校验」放进发布检查。

坑五:长连接数打满 ​

  • 现象:客户端报 Client not connected, current status: STARTING;服务端连接数持续上涨;文件句柄耗尽。
  • 证据:服务端连接数面板、ulimit -n、ss -s、客户端重连日志;连接数是否约等于「客户端进程数 × 节点数」。
  • 根因:每个客户端对每个节点维护连接,命名与配置可能各一条;短生命周期客户端反复建连;LB 空闲超时短于保活间隔;9848 只做 HTTP 代理。
  • 处置:9848/9849 用 TCP 四层转发;LB 空闲超时大于客户端保活间隔;复用客户端实例,不要每次请求新建;调大文件句柄与直接内存;给重连加退避和 jitter。

坑六:MySQL 慢查询拖垮配置推送 ​

  • 现象:配置发布超时、推送延迟变大;Nacos 日志出现 dump/DB 异常;控制台变慢。
  • 证据:MySQL 慢查询日志、SHOW PROCESSLIST、连接池等待、config_info/his_config_info 表大小、磁盘水位。
  • 根因:配置发布要写配置表和历史表;长事务/锁等待/磁盘满会让写变慢;历史表无限增长;把 Nacos 当高频 KV 用。
  • 处置:配置库独立并做主从;清理历史、控制发布频率;监控慢查询与连接池;不要直接改库表;发布链路加超时与告警。

坑七:升级后端口/鉴权不兼容 ​

  • 现象:控制台 404;客户端连不上;启动报鉴权/密钥错误;旧脚本调 v1/v2 API 返回 404/410。
  • 证据:访问的是 8848/nacos 还是 8080;application.properties 中的 token/identity;客户端日志;API 路径版本。
  • 根因:3.x 控制台独立到 8080;Admin/Console 鉴权默认开启且必须显式配 token/identity;v1/v2 API 在 3.2 起剥离到 api-legacy-adapter;默认 Namespace 改为 public。
  • 处置:按升级手册逐节点滚动;探针改到 8080 或服务端健康端点;所有节点 token/identity 一致;旧 API 先接适配器再排期迁移;至少升到 3.2.4(3.0.0–3.2.3 存在鉴权作用域问题)。

坑八:客户端版本与服务端不匹配 ​

  • 现象:启动时配置加载失败、注册成功但收不到推送、NoSuchMethodError/序列化异常。
  • 证据:mvn dependency:tree 里的 nacos-client 版本;SCA 版本;服务端版本;启动日志异常栈。
  • 根因:SCA 2025.0.0.0 自带 nacos-client 3.0.3,要求 3.x 服务端;1.x 客户端连 3.x 需要适配器;多个依赖间接引入不同 client 版本。
  • 处置:用 BOM 锁版本,排除旧 client;按官方兼容矩阵对齐 SCA/Boot/Nacos;升级前在测试环境验证注册、配置、推送三条链路。

最小上手 ​

复现「临时实例消失」和「配置坐标错位」两个最常被误判的问题:

bash
# 1. 注册临时实例后不发心跳,观察 15s/30s 两个节点
curl -s -X POST "http://127.0.0.1:8848/nacos/v3/client/ns/instance" \
  -H "accessToken: $ACCESS_TOKEN" \
  -d "serviceName=order-service" -d "ip=127.0.0.1" -d "port=8080" \
  -d "namespaceId=public" -d "ephemeral=true"

# 2. 把同一份配置写到另一个 namespace,应用仍指向 public
curl -s -X POST "http://127.0.0.1:8848/nacos/v3/admin/cs/config" \
  -H "accessToken: $ACCESS_TOKEN" \
  --data-urlencode "dataId=order-service.yaml" \
  --data-urlencode "groupName=DEFAULT_GROUP" \
  --data-urlencode "namespaceId=dev" \
  --data-urlencode "content=feature.newCheckout: true"

第一项能建立「注册 ≠ 永久存在」的直觉;第二项能复现「控制台明明有配置,应用就是读不到」。两项都确认后再去看更复杂的连接与数据库问题。

生产动作 ​

  • 版本先对齐:Nacos 3.2.4、SCA 2025.0.0.0(Boot 3.5)或按官方矩阵选型;用 mvn dependency:tree 确认只有一个 nacos-client。
  • 端口与探针:8848/9848/9849 全放通;控制台探针指 8080,客户端探针指 9848 或服务端健康端点。
  • 鉴权基线:token/identity 全节点一致;默认口令必须初始化;Registry/控制台不暴露公网。
  • 坐标基线:Namespace 用 ID;启动日志打印实际 namespace/group/dataId;发布前自动核对。
  • 容量基线:连接数按「客户端 × 节点 × 模块」估;LB 空闲超时大于保活间隔;文件句柄与直接内存显式设置。
  • 变更基线:配置发布带 cas、走审批;升级前备份 MySQL 并做恢复演练;保留上一版本的回滚路径。

边界 ​

  • 不是所有「实例消失」都是 Nacos 问题:客户端 GC、容器被杀、网络分区、LB 超时都会表现成一样;先证明链路,再归因。
  • 不是所有「配置不生效」都是刷新问题:坐标、导入方式、优先级、快照都可能;按「坐标 → 导入 → 刷新 → 优先级 → 快照」顺序查。
  • 升级不能只看版本号:控制台端口、鉴权、API、Namespace 都是行为变更;按升级手册逐项验证。
  • 3.2.4 不是终点:安全补丁会继续出;停在旧补丁版本的风险高于升级风险。
  • 重启不是通用解:重启会放大重连风暴、清空本地缓存,可能把局部问题变成全局问题。

讲清楚 ​

Nacos 的线上问题集中在四类错配:把临时实例当成永久存在,忽略心跳和连接的生命周期;配置的 namespace/group/dataId 坐标或刷新方式对不上;接入层只放通 8848、LB 空闲超时打断 9848 长连接,导致重连风暴;客户端与服务端版本不匹配。排查时先分两条线:实例问题先证明 9848 连接,再查心跳、误删和推空保护;配置问题先查坐标,再查导入方式、刷新方式、优先级和本地快照。升级 3.x 要特别注意控制台 8080、鉴权默认开启、v1/v2 API 剥离和 Namespace 改为 public;3.0.0–3.2.3 还有鉴权作用域问题,至少升到 3.2.4。最后,重启不是通用解,它会放大重连风暴并清空缓存,先把链路和坐标证明清楚再动手。

把一门技术讲透到既懂也会用