You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@dubbo.apache.org by "AlexStocks (via GitHub)" <gi...@apache.org> on 2026/04/19 02:58:02 UTC
[D] metadata [dubbo-go]
GitHub user AlexStocks created a discussion: metadata
ref:https://caicaiis.cc/4/dubbo-go-metadata/
GitHub link: https://github.com/apache/dubbo-go/discussions/3300
----
This is an automatically sent email for notifications@dubbo.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@dubbo.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [D] metadata [dubbo-go]
Posted by "AlexStocks (via GitHub)" <gi...@apache.org>.
GitHub user AlexStocks added a comment to the discussion: metadata
文章的宏观主线基本是对的,尤其是“应用级发现里 registry 负责给实例,metadata 负责把实例恢复成服务语义”这条线;但有几处说法讲得太顺了,落到 dubbo-go 现实现状上会失真。
**主要偏差**
- `MetadataService` 不是“provider 默认都会暴露”的能力,它只在 `metadata-type != remote` 时才导出;如果走远端元数据存储,provider 会把 app metadata 发布到 report,但本地不会导出 metadata RPC 服务。[metadata/options.go#L63](/D:/test/github/dubbo-go/metadata/options.go#L63) [service_discovery_registry.go#L92](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L92) [metadata_service_url_params_customizer.go#L58](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/metadata_service_url_params_customizer.go#L58)
- 文章把 `revision` 描述成“当前服务集合的一份摘要”,这个方向对,但实现上没那么强。当前 `resolveRevision` 只用了 `application + path + version + port + methods` 做 CRC 累加,并没有把 `group`、`protocol`、参数集等完整纳入;所以“服务语义变化就一定触发 revision 变化”这个说法,在现实现状下偏强。[service_revision_customizer.go#L87](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/service_revision_customizer.go#L87)
- 文章把 “接口 -> 应用” 映射讲成 metadata 体系里一个自然组成部分,但在 dubbo-go 里它实际上强依赖 `metadata report` 后端。`Map/Get` 都直接走 report;如果没有 metadata report,自动映射就会失败,consumer 只能靠 `provided-by` 这类显式配置兜底。[service_name_mapping.go#L65](/D:/test/github/dubbo-go/metadata/mapping/metadata/service_name_mapping.go#L65) [service_discovery_registry.go#L254](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L254)
- `metadata` 模块不只是维护 provider 暴露服务集合,它也在维护 consumer 的订阅 URL 集合,并计算 `dubbo.subscribed-services.revision`。文章几乎只从 provider 视角讲“服务快照”,少了 consumer 侧这半边状态模型。[metadata.go#L40](/D:/test/github/dubbo-go/metadata/metadata.go#L40) [service_revision_customizer.go#L73](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/service_revision_customizer.go#L73)
- “实例级元数据表达能力偏弱”这个判断可以保留,但文章写得有点过窄。当前实例 metadata 里不只有 `revision` 和 metadata 入口参数,还会写 `dubbo.endpoints`(多协议端口信息)、`meta-v`(metadata service version)、`dubbo.metadata.storage-type` 等字段,所以它不是只有最小二元组,只是离 Java 那种更丰满的实例模型还有距离。[protocol_ports_metadata_customizer.go#L49](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/protocol_ports_metadata_customizer.go#L49) [metadata_service_version_customizer.go#L48](/D:/test/github/dubbo-go/registry/servicediscovery/customizer/metadata_service_version_customizer.go#L48) [service_discovery_registry.go#L117](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L117)
- “接口到应用映射的动态能力不够强”需要加限定条件。不是完全弱,而是“后端实现不均匀”:Nacos 和 Zookeeper 都接了 listener,consumer 订阅链路里也会注册 mapping listener;但 Etcd 实现没有真正处理 listener,所以这个能力不是全局一致的。[service_discovery_registry.go#L246](/D:/test/github/dubbo-go/registry/servicediscovery/service_discovery_registry.go#L246) [nacos/report.go#L134](/D:/test/github/dubbo-go/metadata/report/nacos/report.go#L134) [zookeeper/report.go#L111](/D:/test/github/dubbo-go/metadata/report/zookeeper/report.go#L111) [etcd/report.go#L95](/D:/test/github/dubbo-go/metadata/report/etcd/report.go#L95)
**有一处值得单独强调**
- 文章把 `MetadataService` 更像讲成“远程访问入口”,但在 dubbo-go 代码里,`DefaultMetadataService` 先是一个本地单例状态容器,负责存取 exported/subscribed URLs 和按 revision 查内存中的 `MetadataInfo`;RPC 导出只是它的一层暴露形式,不是它的全部定义。[metadata.go#L27](/D:/test/github/dubbo-go/metadata/metadata.go#L27) [metadata_service.go#L49](/D:/test/github/dubbo-go/metadata/metadata_service.go#L49)
GitHub link: https://github.com/apache/dubbo-go/discussions/3300#discussioncomment-16618679
----
This is an automatically sent email for notifications@dubbo.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@dubbo.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [D] metadata [dubbo-go]
Posted by "AlexStocks (via GitHub)" <gi...@apache.org>.
GitHub user AlexStocks added a comment to the discussion: metadata
一个“dubbo-go metadata 全链路时序图”:
下面这张图按 dubbo-go 当前实现画,把 `provider -> metadata -> service discovery -> consumer` 这条链路串起来了。
```mermaid
flowchart TD
A["Provider 导出服务 URL"] --> B["metadata.AddService(registryId, url)<br/>本地收集 exported URLs"]
A --> C["serviceNameMapping.Map(url)<br/>登记 接口 -> 应用 映射"]
B --> D["生成 MetadataInfo<br/>services / exported URLs / app"]
D --> E["计算 revision<br/>dubbo.metadata.revision"]
E --> F["创建 ServiceInstance"]
F --> G["写入实例 metadata<br/>storage-type / revision / meta-v / endpoints / metadata-service.url-params"]
G --> H{"metadata-type ?"}
H -->|local| I["导出本地 MetadataService RPC<br/>dubbo / tri v1 / tri v2"]
H -->|remote| J["PublishAppMetadata(app, revision, MetadataInfo)<br/>发布到 metadata report"]
I --> K["注册应用实例到 Service Discovery"]
J --> K
L["Consumer 按接口订阅"] --> M{"provided-by 是否显式配置?"}
M -->|是| N["直接得到应用集合"]
M -->|否| O["serviceNameMapping.Get(url, listener)<br/>从 metadata report 查询/监听 接口 -> 应用"]
N --> P["订阅这些应用实例变化"]
O --> P
K --> P
P --> Q["拿到 ServiceInstance 列表"]
Q --> R["读取实例 metadata 中的 revision"]
R --> S{"storage-type ?"}
S -->|remote| T["GetMetadataFromMetadataReport(revision, instance)"]
S -->|local| U["GetMetadataFromRpc(revision, instance)<br/>调用 provider MetadataService"]
T --> V["得到 MetadataInfo"]
U --> V
V --> W["遍历 MetadataInfo.Services"]
W --> X["instance.ToURLs(serviceInfo)<br/>实例 + 服务语义 => 最终 URL"]
X --> Y["形成 consumer 可调用 URL 列表"]
```
如果你想更直观一点,也可以把它压成一句话:
`注册中心给实例,mapping 把接口翻成应用,metadata 再把实例翻成服务 URL。`
再补一张“模块职责图”,方便和上面的时序图对着看:
```mermaid
flowchart LR
A["Service Discovery"] -->|"应用实例列表"| B["Consumer"]
C["Metadata Report"] -->|"app metadata / mapping"| B
D["Provider MetadataService"] -->|"local metadata RPC"| B
E["Provider 本地 metadata"] -->|"生成 revision / services"| A
E -->|"发布 app metadata"| C
E -->|"导出 MetadataService"| D
E -->|"登记 接口 -> 应用"| C
```
最容易记混的边界是这三个:
- `Service Discovery` 负责“发现哪个应用实例在线”
- `Mapping` 负责“这个接口属于哪些应用”
- `Metadata` 负责“这些实例到底暴露了哪些服务,以及怎么恢复成最终调用 URL”
GitHub link: https://github.com/apache/dubbo-go/discussions/3300#discussioncomment-16618685
----
This is an automatically sent email for notifications@dubbo.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@dubbo.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org
Re: [D] metadata [dubbo-go]
Posted by "AlexStocks (via GitHub)" <gi...@apache.org>.
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 notifications@dubbo.apache.org.
To unsubscribe, please send an email to: notifications-unsubscribe@dubbo.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org