Dubbo 从 Zookeeper 迁到 Nacos 后,运维少记了 3 个端口号:两个注册中心的 4 笔账

Dubbo 从 Zookeeper 迁到 Nacos 后,运维少记了 3 个端口号:两个注册中心的 4 笔账 Dubbo 从 Zookeeper 迁到 Nacos 后运维少记了 3 个端口号两个注册中心的 4 笔账一个 Dubbo 项目两台 Zookeeper 服务器五个端口号2018 年进公司接手的第一个 Dubbo 项目。注册中心用的 Zookeeper。运维给了我一张纸条上面写着五个端口号2181ZK 客户端连接2888ZK 集群内部通信Leader ↔ Follower3888ZK Leader 选举20880Dubbo 服务端口22222Dubbo 监控端口我说记不住。运维说习惯了就好。后来切到 Nacos。纸条上的端口号减到两个8848HTTP和 9848gRPC。Dubbo 的服务端口不用和注册中心耦合了——注册信息通过 Nacos 的 API 透传不是像 ZK 那样把 Dubbo URL 直接写进 ZNode 的 data 里。这篇文章不是要证明 Nacos 比 Zookeeper 好。Zookeeper 当了八年 Dubbo 默认注册中心它在一致性上的能力至今没人能替代。但注册中心的角色正在被重新定义——不再是一个分布式协调器而是服务治理平台的一个模块。Nacos 对这个定义的理解恰好踩中了 Dubbo 3.x 时代的需求。⚠️ 本文对比基于 Zookeeper 3.8.x 和 Nacos 2.3.x2024年12月发布。如果你还在用 ZK 3.4.x本文的 ZooDefs 和 session 机制描述仍然适用。全景四个维度两笔旧账NacosAPCP 可切换服务模型Namespace→Group→Service显式心跳 服务端探测注册配置治理一个控制台ZookeeperCPZab 协议树状存储模型Session 心跳只做协调不做治理两个系统在注册中心这个角色上的根本分歧Zookeeper 是一棵分布式协调树Nacos 是一个服务治理平台。前者精确但窄后者宽但运维重。 前面拆过 Nacos 的 AP/CP 分叉源码临时实例走左边持久化走右边——拆开那 3 行 if账本一数据模型——树 vs 服务ZK 的存储模型是一棵树。每个节点叫 ZNode有路径、有数据、有子节点。Dubbo 把服务注册信息存成这样的结构/dubbo/com.example.OrderService/providers/ ├── dubbo://10.0.1.10:20880/com.example.OrderService?... ├── dubbo://10.0.1.11:20880/com.example.OrderService?...每一个 provider URL 是一个临时 ZNode。客户端会话断了ZK 自动删掉这个 ZNode消费者 watcher 收到通知更新本地列表。这套机制的问题是ZNode 里存的是 Dubbo URL不是服务实例。你想在注册中心层面做权重调整、流量路由、元数据过滤必须先解析 URL 字符串改它的参数再写回 ZNode。没有控制台能帮你做这件事。Nacos 的存储模型是服务→实例Namespace: prod ├── Group: DEFAULT_GROUP │ ├── Service: com.example.OrderService │ │ ├── Cluster: DEFAULT │ │ │ ├── Instance: 10.0.1.10:20880 │ │ │ │ ├── weight: 100 │ │ │ │ ├── healthy: true │ │ │ │ ├── metadata: {version: v1} │ │ │ ├── Instance: 10.0.1.11:20880这是一个专门为服务发现设计的数据模型。实例有独立的属性字段权重、健康状态、元数据不是塞在一个 URL 字符串里。你可以在 Nacos 控制台直接调整某个实例的权重——不用解析 URL、不用重写 ZNode。账本二一致性——CP 的代价ZK 用 Zab 协议保强一致。每条写入都要过半节点确认。这在注册中心场景下带来三个实际问题问题一写不可用。3 节点的 ZK 集群挂 2 台剩余的节点进入只读模式——不能注册新服务不能下线实例。Dubbo 的注册写入直接失败抛KeeperException.ConnectionLossException。问题二Session 过期风暴。ZK 的心跳是 Session 机制。客户端和服务端之间有一个 sessionTimeout默认 40 秒。如果网络闪断超过 40 秒ZK 认为客户端挂了删掉它名下所有临时 ZNode。Dubbo 侧表现为这个客户端注册的所有服务同时消失——然后网络恢复所有服务同时重新注册。集群短时间内承受大量写入。问题三Watcher 是一次性的。ZK 的 watcher 触发一次后就失效了。Dubbo 客户端每次收到通知后要重新注册 watcher。大量实例变更时watcher 的注册和触发会产生雪崩效应。Dubbo 2.7 之后做了 watcher 的批量处理和重试机制来缓解这个问题但机制本身的一次性特点没变。Nacos 2.x 对这三个问题的处理方式不同临时实例走 DistroAP3 节点挂 2 台仍可写入上一篇 5.4 里有那 3 行 if。心跳是显式上报客户端主动发不是 Session 机制。超时时间默认 15 秒可以按服务调。订阅是持续监听gRPC 双向流不是一次性 watcher。不用每次重新注册。账本三配置管理——ZK 能做但像用螺丝刀切菜ZK 最早被用作配置中心——ZNode 存配置内容客户端 watch ZNode变更后收到通知。阿里巴巴早期的 Diamond 配置中心就是基于这个思路。但 ZK 做配置管理有几个硬伤没有版本历史。改完就覆盖了回滚只能靠外部备份。没有灰度发布。所有 watch 该 ZNode 的客户端同时收到变更通知不能分批推。没有审计追踪。谁在什么时间改了什么、改之前的值是什么——查不到。大数据量不适合 ZK 的 ZNode 模型。ZK 的单个 ZNode 数据量上限是 1MB。超过 1MB 的配置文件比如一个巨大的 JSON 或 YAML要拆成多个 ZNode。Nacos 的配置管理是独立模块。版本历史、一键回滚、Group 分层灰度、变更审计——这些在 ZK 那边需要二次开发的功能在 Nacos 这边是开箱即用的。账本四运维——少记 3 个端口多维护 1 个 MySQL运维动作ZookeeperNacos基于 2.3.x部署复杂度1 个 tar.gz解压即用需要 MySQLcluster 模式多一个 gRPC 端口 9848集群搭建zkServer.sh start zoo.cfg 配 peercluster.conf 手动配置 MySQL 初始化JVM 调优堆内存 GC 调优ZK 对 GC 敏感堆内存 gRPC 线程池控制台❌ 无内置需 zkUI / zkWeb✅ 完整服务配置管理控制台端口数3 个2181/2888/38883 个8848/9848/9849外部依赖无MySQL运维成本其实是 Nacos 输掉的一笔账。ZK 零外部依赖——一个 JVM 就能跑集群搭建基本没有前置步骤。Nacos 需要 MySQLcluster 模式下还要初始化和维护。但 Dubbo 用户迁移到 Nacos 后运维总成本通常是下降的。原因是用 ZK 做注册中心意味着你要另外选型配置中心Apollo / Consul / 自研选型消息队列、选型监控平台。每多一个组件运维多维护一套。Nacos 把注册和配置合并后虽然 Nacos 本身运维比 ZK 重但省掉了配置中心那套环境。⚠️ 适用边界如果你的团队已经有成熟的配置中心比如 Apollo且没有迁移计划Zookeeper Apollo 是稳定的组合不需要为了统一而推倒重来。一张图带走选 ZK 还是 NacosDubbo 2.7 以下只需注册中心Dubbo 3.x只需注册中心Dubbo 任意版本需要注册配置Spring Cloud 体系你用什么框架?有什么需求?Zookeeper稳定可靠零外部依赖Nacos 或 ZK都可Nacos 原生适配更好Nacos唯一选择ZK 不适合做配置中心NacosSpring Cloud Alibaba默认选择⚠️ ZK 3.8.xsessionTimeout 设为 40s开启 reconfig 功能⚠️ 需要额外维护 MySQL端口 884898489849截图保存。三条判断标准Dubbo 版本、是否需要配置中心、是不是 Spring Cloud 体系。每条路径下面附了运维注意事项。不适用 ZK→Nacos 迁移的两种场景场景一集群体量巨大但只做注册已有成熟的配置管理。如果你的 ZK 集群已经稳定跑了三年、部署了几百个 Dubbo 服务、配置中心用 Apollo 且满意——不需要迁。迁移的风险数据不一致、中间状态、回滚验证大于收益。场景二对 ZK 的强一致性有强依赖。如果你的 Dubbo 服务注册必须强一致不能容忍任何节点间数据不一致Nacos 的临时实例Distro AP 模式不适合。持久化实例可以用 RaftCP但 Nacos 的 Raft 实现只用于持久化实例和服务元数据不像 ZK 那样所有写入都过 Zab。你们 Dubbo 项目用什么注册中心评论区留数字1Zookeeper 稳如老狗 2Zookeeper→正在迁 Nacos 3已切 Nacos 4Nacos Zookeeper 双注册过渡中。顺便说说迁移过程中最让你头疼的那一步。