问题描述
我使用 NacosNamingService.register_instance() 注册了一个临时实例,metadata 里带了 Dubbo 3 协议需要的字段(如 interface、methods、version 等)。
现象:首次注册成功后,如果我通过 Nacos 控制台手动修正了 metadata,或者重新调用 register_instance() 更新了 metadata——只要 gRPC 连接发生断连重连,redo_service 就会用第一次注册时缓存的旧 Instance 数据重新注册,覆盖掉我修正后的 metadata。
阅读源码后确认了原因:
源码分析
register_ephemeral_instance() 位于 v2/nacos/naming/remote/naming_grpc_client_proxy.py:126:
async def register_ephemeral_instance(self, service_name, group_name, instance):
# ① 先把 Instance(含 metadata)缓存到 redo
await self.redo_service.cache_instance_for_redo(service_name, group_name, instance)
# ② 发送注册请求
request = InstanceRequest(...)
response = await self.request_naming_server(request, InstanceResponse)
# ③ 标记为已注册
await self.redo_service.instance_registered(service_name, group_name)
cache_instance_for_redo() 位于 v2/nacos/naming/redo/naming_grpc_redo_service.py,把 Instance 原样存入 _redo_data_map。
当连接断开时,AbstractRedoService.on_disconnect() 只把所有缓存条目标记为"未注册",但 Instance 数据本身不更新。重连后,process_instance_register_redo_type() 直接用这个旧的 Instance 重新注册:
async def process_instance_register_redo_type(self, instance_redo_data):
data = instance_redo_data.get() # ← 缓存的旧 Instance,metadata 是旧的
await self.proxy.register_instance(service_name, group_name, data)
关键矛盾:nacos_naming_service.py 提供了 update_instance() 方法(它内部也调 register_instance(),也会 cache_instance_for_redo() 覆盖缓存),但如果用户是通过以下方式修改 metadata,缓存就不会更新:
- 通过 Nacos 控制台手动改 metadata —— SDK 不知道,缓存不变
- 外部 API / 另一个 SDK 修改 —— SDK 不知道,缓存不变
- 先
deregister_instance() 再 register_instance() —— deregister 把缓存标记为"预期已注销",但 register 时缓存会被新 Instance 覆盖(这个路径理论上可以,但存在时序窗口)
复现步骤
- Python 进程调用
register_instance(),metadata 为 {"version": "1.0"}
- 在 Nacos 控制台把该实例的 metadata 改成
{"version": "2.0"}
- 断开 gRPC 连接(kill Nacos 再重启,或等网络空闲超时)
- Python SDK 重连后,Nacos 控制台的 metadata 又变回了
{"version": "1.0"}
期望行为
至少提供一种方式让用户可以更新 redo 缓存中的 Instance metadata,例如:
update_instance() 应该被文档明确推荐
- 或者提供一个
refresh_redo_cache() 公开方法
- 或者在 redo 重注册前,先从 Nacos 服务端拉取当前 Instance 做对比
当前 Workaround
只能杀掉 Python 进程重开,让 redo 缓存从零开始,用正确的新 metadata 注册。
影响
所有需要运行时调整 metadata(如 Dubbo 协议参数、灰度标签、权重等)的场景都会受影响——redo 会悄无声息地给你覆盖回去。
问题描述
我使用
NacosNamingService.register_instance()注册了一个临时实例,metadata 里带了 Dubbo 3 协议需要的字段(如interface、methods、version等)。现象:首次注册成功后,如果我通过 Nacos 控制台手动修正了 metadata,或者重新调用
register_instance()更新了 metadata——只要 gRPC 连接发生断连重连,redo_service就会用第一次注册时缓存的旧 Instance 数据重新注册,覆盖掉我修正后的 metadata。阅读源码后确认了原因:
源码分析
register_ephemeral_instance()位于v2/nacos/naming/remote/naming_grpc_client_proxy.py:126:cache_instance_for_redo()位于v2/nacos/naming/redo/naming_grpc_redo_service.py,把 Instance 原样存入_redo_data_map。当连接断开时,
AbstractRedoService.on_disconnect()只把所有缓存条目标记为"未注册",但 Instance 数据本身不更新。重连后,process_instance_register_redo_type()直接用这个旧的 Instance 重新注册:关键矛盾:
nacos_naming_service.py提供了update_instance()方法(它内部也调register_instance(),也会cache_instance_for_redo()覆盖缓存),但如果用户是通过以下方式修改 metadata,缓存就不会更新:deregister_instance()再register_instance()—— deregister 把缓存标记为"预期已注销",但 register 时缓存会被新 Instance 覆盖(这个路径理论上可以,但存在时序窗口)复现步骤
register_instance(),metadata 为{"version": "1.0"}{"version": "2.0"}{"version": "1.0"}期望行为
至少提供一种方式让用户可以更新 redo 缓存中的 Instance metadata,例如:
update_instance()应该被文档明确推荐refresh_redo_cache()公开方法当前 Workaround
只能杀掉 Python 进程重开,让 redo 缓存从零开始,用正确的新 metadata 注册。
影响
所有需要运行时调整 metadata(如 Dubbo 协议参数、灰度标签、权重等)的场景都会受影响——redo 会悄无声息地给你覆盖回去。