«

Nacos源码学习计划-补档-Nacos服务订阅链路分析纠错

ZealSinger 发布于 阅读:119 技术文档


对于之前在《Nacos源码学习计划-Day05-服务调用时的调用链路(如何获取服务信息)》一文中,在开始介绍Nacos高版本中通过SCLB来实现客户端实例发现,在一开始做高版本和低版本之间的区别总结,提到如下内容

- 旧版本中,优先查本地缓存,然后再Nacos服务端进行辅助和更新,是以本地缓存为主,Nacos服务端为辅,这个点其实很容易看出来会有实时性的问题
- 新版本中,获取实例都是直接从服务端拉取,但是本地缓存依旧存在,成功拉取后会更新本地缓存,只有当远端拉取失败的时候,才会用缓存数据进行兜底(类似于Leaf的那种buffer机制,为了减少对服务端的绝对依赖,当Nacos服务挂了)
- 从上述变化中可以看到,就是对于实时性和性能之间的优先级的权衡变化

上述对比不准确且有错误,没有详细的区分订阅模式和非订阅模式,高低版本中缓存的使用其实是类似的,但是其真正的区别在于底层的本质

 

一、核心前提:版本底层架构差异(根源)

版本 通信协议 核心更新机制 缓存核心载体
Nacos 1.4 HTTP 短连接 客户端主动定时轮询(6 秒) hostReactor 下的 serviceInfoMap
Nacos 2.X gRPC 长连接(主)+ HTTP(备) 服务端主动增量推送 serviceInfoHolder 本地缓存

二、Nacos 1.4 订阅 / 非订阅模式汇总

1. 订阅模式(subscribe=true,默认)

维度 核心细节
核心方法 - 入口:selectInstances → hostReactor.getServiceInfo
 
- 兜底查服务端:updateServiceNow(HTTP 查服务端全量数据)
 
- 缓存更新:scheduleUpdateIfAbsent(启动 6 秒定时轮询)
缓存逻辑 ① 优先查本地缓存(serviceInfoMap);
 
② 缓存未命中时,同步调用updateServiceNow查服务端并填充缓存;
 
③ 最终必然返回缓存数据;
 
④ 故障时降级读failover本地故障文件;
 
⑤ 无 “服务端查询失败用缓存” 逻辑(缓存未命中时查服务端失败直接抛异常)
底层本质 HTTP 短连接下的「缓存优先 + 客户端定时轮询保鲜」,缓存是主数据来源,服务端查询仅为缓存初始化兜底

2. 非订阅模式(subscribe=false)

维度 核心细节
核心方法 - 入口:selectInstances → hostReactor.getServiceInfoDirectlyFromServer
 
- 底层:namingProxy.queryInstancesOfService(HTTP 查服务端)
缓存逻辑 ① 完全忽略本地缓存,直接 HTTP 查服务端;
 
② 查询结果顺带存入缓存(但不启动定时轮询);
 
③ 无降级逻辑:服务端查询失败直接抛异常,不会读取缓存;
 
④ 下次非订阅查询仍直接查服务端,缓存无实际作用
底层本质 单次 HTTP 短连接查询,追求 “单次实时性”,缓存仅为 “临时存储”,无持续更新

三、Nacos 2.X 订阅 / 非订阅模式汇总

1. 订阅模式(subscribe=true,默认)

维度 核心细节
核心方法 - 入口:selectInstances → serviceInfoHolder.getServiceInfo
 
- 缓存未命中:clientProxy.subscribe(gRPC 初始化订阅)
 
- 增量更新:服务端 gRPC 推送(无需客户端轮询)
缓存逻辑 ① 优先查本地缓存(serviceInfoHolder);
 
② 缓存未命中时,调用subscribe(建立 gRPC 长连接 + 拉取服务端全量数据填充缓存);
 
③ 后续服务端实例变更主动推送增量数据更新缓存;
 
④ 高版本增加 “缓存时效性校验”:异步更新过期缓存,仍优先返回缓存;
 
⑤ 最终必然返回缓存数据
底层本质 gRPC 长连接下的「缓存优先 + 服务端主动推送」,缓存是主数据来源,全量拉取仅初始化,增量推送保证实时性

2. 非订阅模式(subscribe=false)

维度 核心细节
核心方法 - 入口:selectInstances → clientProxy.queryInstancesOfService
 
- 底层:优先 gRPC、备 HTTP 查服务端
缓存逻辑 ① 直接查服务端最新数据(忽略缓存);
 
② 查询成功后,更新本地缓存;
 
③ 服务端查询失败时,降级读取缓存(容错设计);
 
④ 无定时 / 推送更新缓存
底层本质 单次 gRPC/HTTP 查询,追求 “单次实时性 + 容错”,缓存作为降级兜底

四、核心差异对比表(快速查阅)

版本 模式 核心方法 缓存核心逻辑 底层本质
1.4 订阅 hostReactor.getServiceInfo 缓存优先,未命中查服务端,定时轮询更新 HTTP 短连接 + 客户端定时轮询
1.4 非订阅 hostReactor.getServiceInfoDirectlyFromServer 直接查服务端,缓存仅临时存储,无降级 单次 HTTP 查询,无容错
2.X 订阅 serviceInfoHolder.getServiceInfo + clientProxy.subscribe 缓存优先,未命中初始化 gRPC 订阅,服务端推送更新 gRPC 长连接 + 服务端主动推送
2.X 非订阅 clientProxy.queryInstancesOfService 直接查服务端,成功更缓存,失败用缓存 单次 gRPC/HTTP 查询,容错降级

总结

  1. 订阅模式(默认):1.4 和 2.X 均为「缓存优先」,核心差异是 “缓存更新方式”(1.4 客户端轮询 vs 2.X 服务端推送);
  2. 非订阅模式:核心差异是 “容错逻辑”(1.4 无降级,失败抛异常;2.X 失败降级用缓存);
  3. 缓存角色:订阅模式下缓存是 “主数据来源”,非订阅模式下 1.4 缓存仅 “临时存储”,2.X 缓存是 “降级兜底”。

编程 Java 项目