Day 08 / 共 20 天 · 第 2 周 服务发现与配置

Nacos 注册表

Nacos 是 Higress 最重用的注册源(阿里生态)。今天拆一个具体 watcher:怎么订阅 Nacos、轮询全量服务、把实例转成 Istio ServiceEntry。三代实现体现能力演进。昨天(Day 07)看的是"Reconciler 怎么管这一群 watcher",今天挑最常用的 Nacos watcher 钻进去看它内部到底怎么干活,为 Day 09"转出的 ServiceEntry 怎么进 Istio config store"铺路。

📍 你在 Higress 源码课的位置(第1周 全景与控制器 · 第2周 服务发现与配置)
D01 全景 D02 启动 D03 Ingress控制器 D04 配置翻译 D05 xDS-over-MCP D06 多注册中心 D07 McpBridge D08 Nacos D09 ServiceEntry D10 配置+证书
💡 本质:轮询 = 定期翻整本通讯录;订阅 = 给某部门留了报信电话 延续 Day 06 的"通讯录/抄录员"类比——Nacos watcher 这个抄录员用两个动作盯通讯录:轮询就像每隔 30 秒把整本通讯录翻一遍,看有没有新增/撤销的部门(服务)订阅则是对每个关心的部门留个报信电话,一旦这个部门有人入职/离职(实例增删),Nacos 立刻打电话通知。轮询管"部门列表"、订阅管"某部门的人员名单",一粗一细,配合无死角。
L01

三代 Nacos

Higress 有三套 Nacos 实现:registry/nacos/(v1 SDK)、registry/nacos/v2/(v2 SDK)、registry/nacos/mcpserver/(MCP Server 发现)。

读法:v1(基础订阅)→ v2(加鉴权 AccessKey/SecretKey + 地址服务器)→ nacos3(v2 + MCP Server 发现,把 Nacos 上注册的 MCP Server 转成服务,AI 网关特性)。今天以 v1 为主讲清核心流程,再看 v2/nacos3 加了什么。
L02

watcher 结构

// registry/nacos/watcher.go:54
type watcher struct {
  provider.BaseWatcher            // 嵌入基类(Day 06)
  apiv1.RegistryConfig
  namingClient naming_client.INamingClient  // Nacos 官方 SDK
}
// 默认:超时 5000ms、刷新间隔默认 30s(下限 10s)、分页 50、Group/Service 连接符 @@
读法:watcher 嵌入 BaseWatcher(复用回调管理)+ 持有 Nacos 官方 SDK 的 INamingClientNewWatcher 用 Option 模式配置(WithNacosNamespaceId 空值默认 public 等)。
L03

Run 轮询

// watcher.go:196 Run()
// 起 ticker 按 NacosRefreshInterval 周期跑
//   先探活设 Status → fetchAllServices() → Ready(true)
"轮询 + 订阅"双管齐下 Nacos watcher 有两个机制:周期轮询(Run 里的 ticker)——定期拉 Nacos 上的全量服务列表,发现"哪些服务新增/消失"(服务级变化);②订阅(subscribe)——对每个服务向 Nacos 注册监听,实例增删时 Nacos 主动推回调(实例级变化)。轮询管"服务列表",订阅管"某服务的实例列表"。两者配合:轮询发现新服务 → 订阅它 → 实例变化实时收到。首次 fetchAllServices 完成后 Ready(true) 报告就绪(Day 06 回调)。
粗+细双机制盯 Nacos Nacos 通讯录 部门/人员 ① 轮询 (30s ticker) 翻整本 → 服务级 diff ② 订阅 (回调) 留报信电话 → 实例级 generateServiceEntry → 共享缓存 + 通知 发现新服务就订阅它;实例一变就转成 ServiceEntry
图注:轮询负责"部门增减"、订阅负责"部门内人员增减",两条线都汇入转换与通知。
L04

fetchAllServices

// watcher.go:212 fetchAllServices()
// 对每个 group 用 GetAllServicesInfo 分页拉全量服务列表
// 与本地 WatchingServices diff:
//   消失的 → unsubscribe;新增的 → subscribe
// shouldSubscribe(:414)过滤掉 consumers: 前缀(Dubbo 消费者,不是服务提供者)
读法:轮询核心:拉 Nacos 全量服务,和本地"正在监听的服务"diff——新增的订阅、消失的退订。过滤 Dubbo 消费者consumers: 前缀)——只关心服务提供者,消费者不是可路由的目标。这是 Nacos + Dubbo 场景的实战细节。
L05

订阅回调

// watcher.go:297 getSubscribeCallback —— 实例变化时被 Nacos SDK 调用
// 拼 host(serviceName.group.namespace.type,下划线转连字符)
// 空实例时删缓存
// 跳过 register-resource=mcp-bridge 的自注册回环
// 否则 generateServiceEntry 后 cache.UpdateServiceWrapper
//   最后 defer w.UpdateService() 通知上层(Day 06 回调)
读法:某服务的实例增删时,Nacos SDK 调这个回调。它把新实例列表转成 ServiceEntry(L06)、写共享缓存、通知上层刷新。"跳过 mcp-bridge 自注册回环"——避免 Higress 自己注册的东西又被自己发现(死循环)。
L06

generateServiceEntry

// watcher.go:331 把 Nacos 实例转成 Istio ServiceEntry
// 每个实例转 WorkloadEntry(IP+端口+Metadata 作 Labels)
// 从 metadata 读 protocol,权重 math.Round(service.Weight)
// ServiceEntry{Location: MESH_INTERNAL, Resolution: STATIC}
转换是关键——异构 → 统一 这是"把 Nacos 的服务"翻译成"Istio 认的 ServiceEntry"的现场。Nacos 的每个服务实例(IP:Port + 元数据)→ Istio 的一个 WorkloadEntry(Istio 课 Day 03 的概念);一个 Nacos 服务 → 一个 ServiceEntry(含所有实例、协议、权重)。Resolution: STATIC 表示端点是明确的 IP(Istio 课 Day 15 的服务发现类型)。转成 ServiceEntry 后,它就融入了 Istio 的配置体系——istiod 拿去生成 Envoy 的 Cluster/Endpoint,和 K8s 服务一视同仁。这就是"多源统一"的落点。
📝 举个例子:Nacos 实例 → Istio ServiceEntry Nacos 上 order-svc 有两个实例:10.0.0.8:8080 (weight=10)10.0.0.9:8080 (weight=10)
每个实例 → 一个 WorkloadEntry(address=IP、ports、labels=Nacos metadata);
整个服务 → 一个 ServiceEntry(hosts=order-svc.…、含 2 个 endpoint、Resolution: STATICLocation: MESH_INTERNAL)。
从此 istiod 看它就跟一个普通 K8s 服务一样,能生成 Envoy 的 Cluster/Endpoint。

👶 小白:为什么要"跳过 mcp-bridge 自注册回环"?

👨‍🏫 老师:Higress 有时会把自己的东西也注册到 Nacos 上(带 register-resource=mcp-bridge 标记)。如果 watcher 又把它当普通服务发现回来、转成 ServiceEntry、再触发推送……推送可能又引起注册变化,绕成一个自己发现自己的死循环。所以回调里一眼认出这个标记就直接跳过——相当于抄录员看到"这条是我自己刚填进去的",就不再重复登记。

L07

v2 / nacos3 演进

registry/nacos/v2/watcher.go:结构多了 addrProvider(地址服务器动态解析 Nacos 集群地址)、mcpWatcher(MCP Server 子 watcher)。ClientConfig 多了 AccessKey/SecretKey/Username/Password(鉴权)。若 EnableMCPServer=true 且类型 nacos3,创建 mcpserver 子 watcher,把 Nacos 上的 MCP Server 转成 ServiceEntry + WasmPlugin 规则。

读法:v2 加了企业级能力(鉴权、地址服务器);nacos3 加了 AI 网关特性(发现 Nacos 上的 MCP Server)。三代演进对应 Higress 从"普通网关"到"AI 网关"的进化——nacos3 能自动发现并接入 MCP 工具服务。核心 fetchAllServices/回调/generateServiceEntry 逻辑三代类似。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • Nacos 三代的区别?
  • watcher 怎么复用 BaseWatcher + 用官方 SDK?
  • "轮询 + 订阅"双机制各管什么?
  • fetchAllServices 怎么 diff?为什么过滤 Dubbo 消费者?
  • generateServiceEntry 怎么把 Nacos 实例转成 ServiceEntry?

✋ 动手

cd /Users/bitmart/work/codes/github/higress-group/higress
grep -n 'func.*Run\|func.*fetchAllServices\|func.*generateServiceEntry\|getSubscribeCallback' registry/nacos/watcher.go
ls registry/nacos/
明天预告 · Day 09服务 → ServiceEntry——发现的服务怎么进入 Istio config store?共享内存缓存(生产者-消费者)、延迟删除、convertServiceEntry、完整数据流。
← Day 07 McpBridge Day 09 · 服务 → ServiceEntry →