GitHub user AlexStocks added a comment to the discussion: metadata
按 Provider 启动 -> 注册 -> metadata 发布/暴露 -> consumer 拉取 -> URL 恢复 这条时序重新梳一遍。 **先定一个一句话** 在 dubbo-go 里,`metadata` 不是“给注册中心补点描述信息”的边角模块,它本质上是应用级服务发现里的“语义补全层”: - registry / service discovery 告诉你“哪个应用实例在线” - metadata 告诉你“这些实例到底提供哪些接口、这些接口该怎么还原成调用 URL” 这条主线文章是说对了的。 **一、metadata 模块里真正存的是什么** 本地核心状态在 [metadata.go](/D:/test/github/dubbo-go/metadata/metadata.go) 和 [metadata_service.go](/D:/test/github/dubbo-go/metadata/metadata_service.go)。 它维护的不是一个单一对象,而是按 `registryId` 分桶的一组 `MetadataInfo`: - provider 暴露的服务 URL 集合 - consumer 订阅过的 URL 集合 - 这些 URL 抽象出来的 `Services` 视图 - 对应的 `Revision` `MetadataInfo` 的结构在 [metadata_info.go](/D:/test/github/dubbo-go/metadata/info/metadata_info.go): - `App` - `Revision` - `Services map[string]*ServiceInfo` - `exportedServiceURLs` - `subscribedServiceURLs` 这里有个很重要的点: `metadata` 不只是 provider 侧“服务清单”,它也保存 consumer 侧订阅集合。这一点文章基本没展开。 **二、Provider 启动时 metadata 做了什么** 从 provider 视角,链路大致是: 1. 导出服务时,把服务 URL 加进本地 metadata 2. 建立接口到应用的映射 3. 生成 revision 4. 把 revision 和若干 metadata 入口信息写到 service instance metadata 5. 如果配置为 `remote`,把整份 app metadata 发布到 metadata report 6. 如果配置为 `local`,额外导出本地 `MetadataService` RPC 服务 对应代码主链路在 [service_discovery_registry.go](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go)。 先看注册单个服务: - `Register(url)` 时调用 `metadata.AddService(...)`,把 provider URL 放进本地 metadata - 然后调用 `serviceNameMapping.Map(url)`,把“接口 -> 应用”关系写入 metadata report 见 [service_discovery_registry.go#L225](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L225)。 之后注册应用实例: - `RegisterService()` 里用本地 `MetadataInfo` 创建 `ServiceInstance` - 把 `dubbo.metadata.revision` 写进实例 metadata - 如果 `metadataType == remote`,调用 `PublishAppMetadata(...)` 发到 metadata center - 再把实例注册到 service discovery 见 [service_discovery_registry.go#L85](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L85)。 **三、revision 在 dubbo-go 里到底是什么** 文章把它叫“服务集合摘要”,方向没错,但在 dubbo-go 里实现比这个描述更窄。 当前 revision 计算在 [service_revision_customizer.go](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/service_revision_customizer.go): 它把每个 URL 变成候选字符串,核心只用了: - `application` - `path` - `version` - `port` - `methods` 然后排序后做 CRC32 累加。 这意味着: - 它确实能反映“服务集合是否变化” - 但它不是完整的“元数据总摘要” - 某些参数变化未必会反映到 revision 上,比如并不是所有 service params 都纳入了 revision 计算 所以更准确的说法是: `revision` 是 dubbo-go 当前用于判定“导出服务视图是否变化”的轻量摘要,而不是对全部 metadata 语义的完整哈希。 **四、MetadataService 是什么,不是什么** 这点很容易被讲歪。 `DefaultMetadataService` 在 dubbo-go 里首先是本地内存态访问器,不是先天的远程服务。 它提供的方法包括: - `GetExportedServiceURLs()` - `GetSubscribedURLs()` - `GetMetadataInfo(revision)` - `GetMetadataServiceURL()` 定义在 [metadata_service.go#L49](/D:/test/github/dubbo-go/metadata/metadata_service.go#L49)。 RPC 暴露只是它的一种外化形式: - `dubbo` 协议导出 v1 - `triple` 协议导出 v1 + v2 见 [metadata_service.go#L171](/D:/test/github/dubbo-go/metadata/metadata_service.go#L171)。 但要注意一个边界: - 当 `metadata-type=local` 时,才会导出 `MetadataService` - 当 `metadata-type=remote` 时,不导出本地 metadata RPC 服务,consumer 要去 metadata report 拉 见 [options.go#L63](/D:/test/github/dubbo-go/metadata/options.go#L63)。 所以更准确的话应该是: metadata 在 dubbo-go 中既可能以“provider 暴露的 RPC 服务”形式被访问,也可能以“metadata center 存储”形式被访问,取决于 storage type。 **五、实例 metadata 里到底写了哪些东西** 文章说 provider 会把 `revision` 和 metadata 入口参数写进实例 metadata,这个是对的,但不完整。 当前至少还有这些关键字段: - `dubbo.metadata.revision` - `dubbo.metadata.storage-type` - `dubbo.metadata-service.url-params` - `meta-v` - `dubbo.endpoints` 对应代码: - revision: [service_revision_customizer.go](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/service_revision_customizer.go) - storage type: [service_discovery_registry.go#L117](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L117) - metadata service url params: [metadata_service_url_params_customizer.go](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/metadata_service_url_params_customizer.go) - metadata version: [metadata_service_version_customizer.go](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/metadata_service_version_customizer.go) - endpoints: [protocol_ports_metadata_customizer.go](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/protocol_ports_metadata_customizer.go) 这说明 dubbo-go 的实例级 metadata 不是“只有 revision 和入口地址”,而是已经有一套最小可运行的实例语义模型,只是比 Java 侧还薄。 **六、接口 -> 应用 mapping 在这里扮演什么角色** 这是应用级发现能工作起来的关键桥。 因为 service discovery 看到的是“应用实例”,但 consumer 常常还是按“接口”发起订阅,所以必须先回答: “这个接口由哪些应用提供?” 这层抽象就是 `ServiceNameMapping`,接口定义在 [service_name_mapping.go](/D:/test/github/dubbo-go/metadata/mapping/service_name_mapping.go),默认实现是基于 metadata report 的 [metadata/service_name_mapping.go](/D:/test/github/dubbo-go/metadata/mapping/metadata/service_name_mapping.go)。 它的语义很清楚: - `Map(url)`:provider 注册时写入“接口 -> 应用” - `Get(url, listener)`:consumer 订阅时查出“这个接口对应哪些应用” - `Remove(url)`:移除监听 但这块有一个比文章更现实的约束: mapping 在 dubbo-go 里不是纯本地能力,它强依赖 metadata report。 没有 metadata report,自动 mapping 会失败,consumer 就只能靠 `provided-by` 显式指定应用。 这点在 [service_discovery_registry.go#L254](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L254) 很明显。 **七、Consumer 运行时到底怎么把实例恢复成服务 URL** 这才是 metadata 最直接的运行时价值。 consumer 订阅链路大致是: 1. reference 按接口发起订阅 2. 先通过 mapping 找到这个接口对应的应用集合 3. 再监听这些应用的实例变化 4. 收到实例列表后,读取每个实例上的 `dubbo.metadata.revision` 5. 根据实例 metadata 判断去哪里拿 metadata 6. 拿到 `MetadataInfo` 后,把 `ServiceInfo + instance` 组合成最终可调用 URL 核心逻辑在 [service_instances_changed_listener_impl.go](/D:/test/github/dubbo-go/registry/servicediscovery/service_instances_changed_listener_impl.go)。 其中最关键两步: - `GetMetadataInfo(...)` 决定元数据来源 如果 `storage-type=remote`,走 `GetMetadataFromMetadataReport` 否则走 `GetMetadataFromRpc` - 拿到 `MetadataInfo` 后,遍历里面的 `Services` - 调用 `instance.ToURLs(serviceInfo)` 恢复出最终 URL 见: - [service_instances_changed_listener_impl.go#L227](/D:/test/github/dubbo-go/registry/servicediscovery/service_instances_changed_listener_impl.go#L227) - [service_instance.go#L166](/D:/test/github/dubbo-go/registry/service_instance.go#L166) 所以可以把 consumer 侧理解成一句话: consumer 不是直接消费“注册中心给的实例地址”,而是消费“实例 + metadata 还原出的服务 URL”。 **八、远端拉取 metadata 时,实际有两条路** 文章提到 metadata center 和 MetadataService 两条路径,这个是对的。代码里也确实是双路: - remote storage:从 metadata report 拉 [client.go#L45](/D:/test/github/dubbo-go/metadata/client.go#L45) - local storage:直接 RPC 调 provider 的 MetadataService [client.go#L53](/D:/test/github/dubbo-go/metadata/client.go#L53) 而且 triple 下会优先区分: - v1 - v2 这不是单纯“远程访问 metadata”,而是“按实例声明的 metadata 版本和存储类型选择访问路径”。 **九、和文章相比,我建议你脑子里换成这版模型** 如果用更贴近 dubbo-go 当前实现的话,我会这样表述: - `metadata` 的核心职责不是“保存元数据”,而是维护一份“应用能提供什么服务”的本地视图,并把它变成可传播、可检索、可恢复的运行时语义。 - provider 侧,metadata 负责收集 exported/subscribed URLs、生成 `MetadataInfo`、计算 revision、登记 interface-app mapping,并把这些信息注入 service instance 或发布到 metadata report。 - consumer 侧,metadata 负责把“接口订阅”转换成“应用集合”,再把“应用实例”恢复成“最终服务 URL 列表”。 - `MetadataService` 不是 metadata 的全部,而是 local storage 模式下的一种远程访问入口。 - `revision` 不是完整 metadata 的强语义哈希,而是 dubbo-go 当前实现下的轻量服务视图摘要。 - mapping 不是一个独立小能力,而是应用级发现和接口级调用之间真正的桥;但在 dubbo-go 里它强依赖 metadata report。 - 实例 metadata 已经有最小但成体系的表达:revision、storage type、meta version、metadata service params、protocol endpoints。 **最后给一个最简版时序** 你可以直接拿这 8 步理解整个模块: 1. provider 导出服务 2. 本地 metadata 收集 exported URLs 3. 计算 revision 4. 写入 interface -> app mapping 5. 把 revision / endpoint / metadata params 写入 service instance 6. instance 注册到 service discovery 7. consumer 通过 mapping 找到应用,再拿到实例 8. consumer 按 revision 拉 metadata,并还原成最终服务 URL GitHub link: https://github.com/apache/dubbo-go/discussions/3300#discussioncomment-16618681 ---- This is an automatically sent email for [email protected]. To unsubscribe, please send an email to: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
