首页
/ Nacos Naming 元数据与选择器规范深度解析:从三级元数据模型到 Selector 分类与过期清理机制

Nacos Naming 元数据与选择器规范深度解析:从三级元数据模型到 Selector 分类与过期清理机制

2026-09-09 16:21:34作者:史锋燃Gardner

本文以 Nacos 仓库中 Naming Metadata And Selector Spec 为骨架,系统讲解 Naming 模块的元数据层级(Service / Cluster / Instance 三级)、运行时元数据与运维元数据的优先级规则、保留元数据键(含 Agent Endpoint 命名空间)、三类 Selector 的边界划分,以及元数据持久化与过期清理的实现原理。读完本文,你将能准确回答"哪些元数据可以写、哪些键被保留、运维覆盖如何生效、选择器各司其职的分工是什么、元数据何时会被清理",并能结合仓库源码定位每一环节的实际实现。

1. 元数据的三级模型:Service、Cluster、Instance

Naming 元数据存在于三个资源层级,每一级有明确的职责范围和"属主"(谁有权限写):

层级 覆盖范围 属主
Service 元数据 服务级发现元数据、保护阈值(protect threshold)、遗留 selector 字段、集群映射(cluster map) Admin API、Console API、Maintainer SDK
Cluster 元数据 健康检查器(health checker)、检查端口行为(check port behavior)、集群扩展元数据 Admin API、Console API、Maintainer SDK
Instance 元数据 实例权重(weight)、启用状态(enabled)、扩展元数据(extended metadata) 运行时注册与管理 API

从源码结构看,这三个层级在 naming 模块中有独立的领域模型:

  • ServiceMetadata.java 持有 ephemeral(默认 true)、protectThreshold(默认 0.0F)、selector(默认 NoneSelector)、extendDataclusters 映射;
  • ClusterMetadata.java 持有 healthyCheckPort(默认 80)、healthyCheckType(默认 Tcp)、healthChecker(默认 new Tcp())、useInstancePortForCheck(默认 true)与 extendData
  • InstanceMetadata.java 持有 weight(默认 1.0D)、enabled(默认 true)与 extendData

规范明确规定:元数据变更不改变服务的身份(identity)。元数据变更应当发布服务或实例信息变更事件,以便存储索引、推送(push)与诊断(diagnostics)及时刷新。本地事件投递由 Event Dispatch And NotifyCenter Spec 定义——这正是 NamingMetadataManager.java 中订阅 MetadataEvent.InstanceMetadataEventMetadataEvent.ServiceMetadataEvent 以及 ClientEvent.ClientDisconnectEvent 三类事件的依据。

2. 两类元数据来源与"运维覆盖运行时"优先级

Naming 区分两种元数据来源,它们的含义、持久化方式和优先级截然不同:

来源 含义 持久化 优先级
运行时元数据(Runtime metadata) 运行时发布者在实例注册或心跳时提交的元数据,主要描述注册进程自己控制的部署期与运行期状态 绑定在运行时发布者及其服务类型上 较低
运维元数据(Operational metadata) 通过 Nacos 管理路径(Admin API、Console API、Maintainer SDK、元数据持久化)写入的元数据,代表运维人员或开发者的意图 由 Nacos 存储,可在运行时客户端消失后继续存活,直到清理规则生效 较高

核心优先级规则:当同一元数据键同时存在于运行时元数据与运维元数据中时,服务视图中必须采用运维值。 原因是运维元数据是显式的管理覆盖(management override),必须由 Nacos 持久化或记忆(memoize)。

在服务级,正式的服务元数据本身就是运维元数据;在实例级,运行时注册元数据是基础视图,运维实例元数据在它之上做叠加(overlay)。这一设计使得运维人员可以在不修改客户端进程的前提下,覆盖实例的权重、启用状态等运行期属性。

3. 保留元数据键:核心行为不得绑定用户自定义键

大部分元数据是用户自定义的键值数据,但 Nacos Naming 为以下实例元数据键保留了核心行为语义,这些键统一定义在 PreservedMetadataKeys.java

含义
preserved.register.source 实例的注册来源,如 Dubbo、Spring Cloud 等框架标识
preserved.heart.beat.interval 心跳间隔覆盖值
preserved.heart.beat.timeout 心跳不健康超时覆盖值
preserved.ip.delete.timeout 心跳删除超时覆盖值
preserved.instance.id.generator 实例 ID 生成器选择

从源码看,这五个常量自 1.0.0 版本起被 com.alibaba.nacos.api.naming.PreservedMetadataKeys 公开,客户端与服务端共同识别。规范强调:新的核心行为不得绑定到任意用户元数据键上;如果某个元数据键会改变 Naming 行为,它必须先被保留(reserved)并记录到规范中,否则一律视为普通用户数据。

4. Agent Runtime Endpoint 保留命名空间:__nacos.agent.endpoint.*__

完整的 __nacos.agent.endpoint.*__ 命名空间为 Agent Runtime Endpoint 投影(projection)保留,其版本 1 的键如下:

含义
__nacos.agent.endpoint.path__ URI 路径
__nacos.agent.endpoint.transport__ 规范传输方式,必须与 Naming 集群一致
__nacos.agent.endpoint.protocol__ URI scheme,而非 Agent CallInterface 协议 token
__nacos.agent.endpoint.protocolVersion__ 可选的遗留 A2A 协议版本兼容事实
__nacos.agent.endpoint.supportTls__ 投影 URI 是否使用 TLS
__nacos.agent.endpoint.query__ 原始 URI 查询串
__nacos.agent.endpoint.tenant__ 存在时的协议原生租户
__nacos.agent.endpoint.version__ 该 Instance 贡献的运行时版本(Runtime Version)
__nacos.agent.endpoint.versionRange__ 该 Instance 贡献的规范版本范围(canonical Version range)
__nacos.agent.endpoint.priority__ Endpoint 优先级,数字越小优先级越高

这些键在仓库中由 AI 模块常量类 Constants.javaAgent 内部类逐一声明(AGENT_ENDPOINT_METADATA_PREFIX = "__nacos.agent.endpoint.",配合 path__transport__protocol__protocolVersion__supportTls__query__tenant__version__versionRange__priority__ 后缀拼接而成),并配套 AGENT_ENDPOINT_GROUP = "agent-endpoints" 分组常量。

该命名空间有严格的写入约束:

  • 只有 Agent Endpoint Naming 适配器可以写入此前缀下的键;公开的运行时元数据与运维元数据写入必须拒绝这些键,以避免普通"运维覆盖运行时"的优先级规则覆盖 Agent 投影事实。
  • Endpoint 的权重(weight)、启用状态(enabled)与健康状态(health)继续使用 Naming Instance 原生字段,而不是保留元数据键。

规范同时定义了版本相关的 A2A / RAD 兼容布局:

  • 当前特定版本的 A2A 兼容布局可以写 protocolVersion;新的 RAD 注册不写该键。公开的 RAD Endpoint 元数据与运行时修订(Runtime revision)均排除它。旧的 A2A 响应投影优先使用该值,缺失时回退到目标 Agent CallInterface 的 protocolVersion
  • 每个新的版本中立(Version-neutral)RAD Runtime Naming Instance 恰好携带一对 versionversionRange,且范围必须是规范的并包含运行时版本。Naming 元数据不存储序列化的 bindings 数组;当前特定版本的 A2A Naming 布局不受此约束。

注册遵循 Naming 的完整批次替换(complete-batch replacement)语义:客户端维护一个连接对一个 Agent 协议服务发布的完整 Endpoint 批次,在本地删除或替换条目后,通过 Naming 批量注册提交剩余的完整批次;如果期望批次为空,客户端调用整体注销(whole-publication deregistration)而不是提交空批次。服务端 Agent 适配器将提交的批次映射为 Naming Instances,且不得读取并合并发布者之前的批次

在 RAD Runtime 查询时,读取方根据每个 Instance 的单一 version/versionRange 对构造一个 RuntimeVersionBinding,应用版本范围匹配,再按自然 Endpoint 键聚合出 bindings[]RuntimeVersionBindingbindings[] 都是查询投影(query projections),而非 Naming 注册元数据;精确的 Runtime 投影规则由 Agent Storage Spec 定义。

5. Selector 三大类别:边界、职责与源码印证

Naming 目前存在三种 selector 概念,规范明确区分了它们的边界,防止互相污染:

类别 范围 规范状态
内部实例过滤(Internal instance filtering) 服务端实现层的过滤,塑造发现视图,如 cluster、enabled、health、保护阈值及内部过滤钩子 正式 Naming 行为
API 定义的服务 selector(API-defined service selector) 旧服务 API 与 SDK maintainer 方法接受的遗留服务 selector 输入 仅兼容,待移除
客户端 selector(Client-side selector) SDK 侧的 NamingSelector,用于本地 subscribe/unsubscribe 与监听器匹配 正式 SDK 扩展行为

5.1 内部实例过滤:服务端发现语义的一部分

内部实例过滤属于服务端发现语义,必须保持其他 Naming 规范定义的 service、cluster、instance、health、enabled、service type 与保护语义。其核心实现位于 ServiceUtil.javadoSelectInstancesselectInstancesWithHealthyProtection

  • 按 cluster 集合过滤(checkCluster,空集合视为不过滤);
  • 按 enabled 过滤(checkEnabled,仅当 enableOnly 为真时生效);
  • 按 healthy 过滤(healthyOnly 为真时仅保留健康实例),同时统计健康实例数;
  • 保护阈值逻辑:当 newHealthyCount / allInstances.size() <= protectThreshold 时触发保护,返回全部 IP,并把不健康实例"深度拷贝后置为 healthy"以保护可用性(setReachProtectionThreshold(true) 并记录 protect threshold reached 日志);
  • 在保护阈值计算前,还会通过 SelectorManager.select(serviceMetadata.getSelector(), subscriberIp, allInstances) 应用服务级 selector 过滤,并重新计算健康计数。

该文件还负责将 ServiceDetailInfo 转换为控制台旧版 UI 所需的视图结构(transferToConsoleResult),把 useInstancePortForCheckhealthyCheckPorthealthChecker 等集群元数据输出为控制台可读的 Cluster 对象。

5.2 API 定义的服务 selector:仅兼容、待移除

遗留服务 selector 字段被旧版服务 API 与 SDK maintainer 方法接受,属于兼容性行为。规范明确:不得用它定义任何新的服务端行为,新 API 与规范应显式建模过滤,或使用客户端 selector 处理 SDK 本地的行为。ServiceMetadataselector 字段的类型来自 com.alibaba.nacos.api.selector.Selector,默认值为 NoneSelector。其移除节奏需遵循 Compatibility And Deprecation Spec

5.3 客户端 selector:SDK 本地扩展点

客户端 selector 是 SDK 扩展点,只过滤本地监听器通知或选择结果,不得修改服务端的 service、instance、metadata 或一致性状态。相关实现分散在 client 模块:

  • NamingSelector.java:API 侧接口,继承 Selector<NamingContext, NamingResult>,其中 NamingContext 提供 serviceName、groupName、clusters 与实例列表;
  • DefaultNamingSelector.java:默认实现,用一个 Predicate<Instance> 对流式过滤实例列表;
  • NamingSelectorFactory.java:工厂类,提供常用选择器——EMPTY_SELECTOR(原样返回)、HEALTHY_SELECTORInstance::isHealthy)、newClusterSelector(clusters)(按集群名集合过滤)、newIpSelector(regex)(按 IP 正则匹配)、newMetadataSelector(metadata[, isAny])(按元数据键值匹配,isAny=false 时要求全部匹配,true 时任一匹配即可);
  • NamingSelectorWrapper.java:把 selector 与 EventListener 包装在一起,订阅时通过 SelectorManager 注册;实例变更事件到达时,对当前实例、新增、移除、修改的实例分别执行 doSelect(即 selector 的 select 过滤),再产出 NamingChangeEvent 通知监听器。

InstancesChangeNotifier.java 可以看到客户端 selector 的注册入口:registerSelector(subId, wrapper) / deregisterSelector(subId, wrapper),其内部 selectorManager.isSubscribed(subId) 判断是否已订阅。这套机制让开发者可以在不改动服务端的前提下,实现按集群、IP 正则或自定义元数据维度"订阅并只关心自己想要的实例变化"。

6. 集群健康检查器元数据

集群元数据控制实际的健康检查行为,具体包括:

  • checker 类型与序列化的 checker 字段;
  • 是否使用实例端口,还是使用固定检查端口;
  • 集群级扩展元数据。

ClusterMetadata.java 中对应为 healthyCheckTypehealthChecker(类型为 AbstractHealthChecker,默认 Tcp)、useInstancePortForCheck(默认 true,即默认用实例端口做检查)与 healthyCheckPort(默认 80,当不使用实例端口时生效)。这些字段被 HttpHealthCheckProcessorTcpHealthCheckProcessor 等健康检查处理器读取使用。

规范特别强调:健康检查器元数据属于集群,绝不能复制进实例身份或服务身份。这保证了健康检查配置的层级边界,避免元数据在不同资源层级间相互污染。

7. 元数据持久化与过期清理:CP 路径与"实例身份"判定

服务元数据、集群元数据与实例元数据的写操作都走 CP 元数据路径。元数据可能暂时比运行时客户端存活得更久;过期元数据清理(expired metadata cleanup)会在其所属的服务或实例脱离(detached)超过配置的过期窗口后删除元数据。

内存侧的状态管理由 NamingMetadataManager.java 完成:它维护 serviceMetadataMapConcurrentMap<Service, ServiceMetadata>)与 instanceMetadataMapConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>>),并通过 updateServiceMetadata / updateInstanceMetadata / removeServiceMetadata / removeInstanceMetadata 提供读写入口,同时管理 expiredMetadataInfos 集合。

关键设计点是 实例元数据以实例身份(instance identity)标识,而不是以发布客户端标识

  • 元数据 ID 由 InstancePublishInfo.genMetadataId(ip, port, clusterName) 生成(ip:port:cluster 形式);
  • 客户端断开本身并不解除实例——同一实例可能已被另一个客户端重新注册;
  • 因此清理前必须确认该实例在其服务中已不再注册,才能删除元数据;如果实例仍在注册,则保留元数据并停止追踪这条过期记录。

这一逻辑完整体现在 ExpiredMetadataCleaner.java 中:doClean() 遍历过期元数据集合,当 currentTime - createTime > getExpiredMetadataExpiredTime() 时执行 removeExpiredMetadata;对于实例元数据,先通过 isInstanceStillRegistered 用当前 ServiceStorage 推送数据中的每个实例重建 metadataId 并比对(从实例重建 ID 而非从过期 ID 反解,可正确处理含冒号的 IPv6 地址),若仍在注册则保留。

相关配置项定义在 Constants.java 并由 GlobalConfig.java 读取:

配置项 含义 默认值
nacos.naming.clean.expired-metadata.interval 过期元数据清理定时任务的执行间隔(毫秒) 5000 ms
nacos.naming.clean.expired-metadata.expired-time 过期元数据到期时间(毫秒),即脱离后保留多久 60000 ms

清理任务在 ExpiredMetadataCleaner 构造时通过 GlobalExecutor.scheduleExpiredClientCleaner 调度,初始延迟 5000 ms。运行时元数据跟随运行时发布者的生命周期;运维元数据则走元数据持久化路径,在恢复后可以叠加到运行时元数据之上。

8. 待移除项与相关规范

待移除(Pending Removal):API 定义的服务 selector 字段与请求参数属于遗留兼容行为。应在新的 API 与 SDK 规范中将其标记为废弃(deprecated),并在兼容性要求允许后,从正式 Naming 行为中移除,具体节奏遵循 Compatibility And Deprecation Spec

相关规范:本文涉及的多项能力与其他规范互相引用,建议按需延伸阅读:

9. 实践要点速查

  1. 写元数据前先查保留键preserved.*__nacos.agent.endpoint.*__ 前缀下的键分别由 PreservedMetadataKeys 与 Agent Endpoint 适配器专享,公开写入会被拒绝。
  2. 运维覆盖是"单向优先":同一键同时存在时,运维元数据覆盖运行时元数据;实例级的基础视图来自运行时注册,运维元数据在其上叠加。
  3. 选择器分工明确:服务端过滤(cluster/enabled/healthy/保护阈值)在 ServiceUtil.java 完成;客户端本地过滤用 NamingSelectorFactory 的组合式选择器;遗留服务 selector 字段仅作兼容。
  4. 清理基于实例身份而非客户端:实例元数据 ID 是 ip:port:cluster,客户端断开不等于实例脱离,清理前会二次确认实例是否仍在注册。
  5. 健康检查配置在集群级:checker 类型、检查端口、是否使用实例端口均属于集群元数据(ClusterMetadata.java),不要复制到实例或服务身份中。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525