Dubbo-go Metadata 设计方案
目标仓库:apache/dubbo-go
基准分支:develop
相关讨论:apache/dubbo-go discussions #3300
相关问题:
1. 背景
Dubbo 3 的服务发现模型正在从 接口级注册 逐步演进到 应用级注册。
在应用级注册模式下,注册中心只应该保存轻量的应用实例信息。完整的服务元数据,例如导出的接口、协议、端口、方法、参数和服务定义,应通过 metadata 系统获取。
dubbo-go 已经具备一套 metadata 基础能力:
metadata/metadata_service.goMetadataServiceDefaultMetadataService- MetadataService V1 导出
- MetadataService V2 导出
metadata/info/metadata_info.goMetadataInfoServiceInfo
metadata/client.go- 本地 metadata RPC 拉取
- 远程 metadata report 拉取
registry/servicediscovery/service_discovery_registry.go- provider 侧应用级服务发现注册
registry/servicediscovery/service_instances_changed_listener_impl.go- consumer 侧实例变更处理
- 按 revision 获取 metadata
- 根据 metadata 生成 provider URL
但是当前 develop 分支仍然存在几个问题:
- MetadataService V1 / V2 的导出策略不够显式。
meta-v可能和实际导出的 MetadataService 版本不一致。metadata-service-protocol已经存在于配置结构中,但没有完整传递到 metadata 初始化流程。MetadataInfo的 revision 计算方式和 Java Dubbo 尚未完全对齐。- consumer 侧 metadata 缓存只使用
revision作为 key,可能导致不同应用之间的缓存冲突。 develop分支已经增强了多 provider URL 生成逻辑,但应用级实例语义、endpoint metadata 和回归测试仍需收敛。- dubbo-go 的 MetadataServiceV2 proto 落后于当前 Java Dubbo 契约。
本设计的目标是让 dubbo-go metadata 机制在兼容性、确定性和生产可用性上进一步收敛。
2. 目标
2.1 功能目标
- 支持 provider 侧 MetadataService V1 和 V2 导出。
- 支持
local和remote两种 metadata 存储模式。 - 支持将应用级 metadata 发布到 metadata report。
- 支持 consumer 按应用实例 revision 拉取 metadata。
- 支持 Java Dubbo 3.3 consumer 调用 dubbo-go provider 的 metadata 服务。
- 支持 dubbo-go consumer 调用 Java Dubbo 3.3 provider 的 metadata 服务。
- 支持同一应用下的多个 provider 实例。
- 支持同一应用实例暴露多个协议端口。
- 保证
meta-v严格匹配实际导出的 MetadataService 版本。 - 避免 metadata 拉取失败导致 consumer panic。
2.2 非目标
第一阶段不要求实现完整 OpenAPI 生成能力。
但是 MetadataServiceV2 的 proto 和服务描述应该对齐 Java Dubbo,包含 GetOpenAPIInfo。第一阶段可以先返回空定义。
3. 当前状态
3.1 MetadataInfo
当前 dubbo-go 的 MetadataInfo 模型大致如下:
type MetadataInfo struct {
App string
Revision string
Tag string
Services map[string]*ServiceInfo
exportedServiceURLs map[string][]*common.URL
subscribedServiceURLs map[string][]*common.URL
}MetadataInfo 负责记录当前应用导出和订阅的服务信息。
主要文件:
metadata/info/metadata_info.go3.2 MetadataService
当前 dubbo-go 的 MetadataService 接口大致如下:
type MetadataService interface {
GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]*common.URL, error)
GetExportedServiceURLs() ([]*common.URL, error)
GetSubscribedURLs() ([]*common.URL, error)
Version() (string, error)
GetMetadataInfo(revision string) (*info.MetadataInfo, error)
GetMetadataServiceURL() (*common.URL, error)
}主要文件:
metadata/metadata_service.go3.3 MetadataService V2 Proto
当前 dubbo-go V2 proto 只有:
service MetadataServiceV2 {
rpc GetMetadataInfo(MetadataRequest) returns (MetadataInfoV2);
}主要文件:
metadata/triple_api/proto/metadata_service_v2.proto当前 Java Dubbo V2 proto 包含:
service MetadataServiceV2 {
rpc GetMetadataInfo(MetadataRequest) returns (MetadataInfoV2);
rpc GetOpenAPIInfo(OpenAPIRequest) returns (OpenAPIInfo);
}因此 dubbo-go 应该对齐 Java Dubbo 的 V2 proto 契约。
3.4 ServiceInstance Metadata Keys
dubbo-go 已经定义了关键的实例 metadata 常量:
ExportedServicesRevisionPropertyName = "dubbo.metadata.revision"
SubscribedServicesRevisionPropertyName = "dubbo.subscribed-services.revision"
MetadataStorageTypePropertyName = "dubbo.metadata.storage-type"
MetadataServiceURLParamsPropertyName = "dubbo.metadata-service.url-params"
MetadataServiceURLsPropertyName = "dubbo.metadata-service.urls"
MetadataVersion = "meta-v"
ServiceInstanceEndpoints = "dubbo.endpoints"主要文件:
common/constant/key.go这些 key 与 Java Dubbo 的应用实例 metadata 模型基本一致。
4. 设计概览
metadata 整体流程如下:
核心原则:
- 注册中心实例 metadata 应保持轻量。
- 完整服务信息应保存在
MetadataInfo中。 - consumer 使用
revision判断是否需要重新拉取 metadata。 local模式下,consumer 通过 provider 的 MetadataService RPC 拉取 metadata。remote模式下,consumer 从 metadata report 拉取 metadata。meta-v必须匹配实际导出的 MetadataService 版本。
5. Metadata 存储模式
5.1 Local 模式
配置:
dubbo:
application:
metadata-type: local行为:
- provider 导出本地 MetadataService。
- provider 在服务实例 metadata 中写入:
dubbo.metadata.storage-type=localdubbo.metadata.revision={revision}dubbo.metadata-service.url-params={...}meta-v=1.0.0或meta-v=2.0.0
- consumer 收到服务实例变更事件。
- consumer 根据实例 metadata 构造 MetadataService URL。
- consumer 调用 MetadataService RPC 获取完整
MetadataInfo。
5.2 Remote 模式
配置:
dubbo:
application:
metadata-type: remote
metadata-report:
protocol: nacos
address: 127.0.0.1:8848行为:
- provider 不需要导出本地 MetadataService。
- provider 将
MetadataInfo发布到 metadata report。 - provider 在服务实例 metadata 中写入:
dubbo.metadata.storage-type=remotedubbo.metadata.revision={revision}
- consumer 通过
app + revision从 metadata report 获取MetadataInfo。
主要文件:
metadata/report_instance.go
metadata/client.go6. MetadataService 版本策略
6.1 问题
一个已知兼容性问题是:
instance metadata:
meta-v = 2.0.0
provider actually exported:
org.apache.dubbo.metadata.MetadataService, version=1.0.0这种情况下,Java Dubbo 3.x consumer 可能会尝试调用:
org.apache.dubbo.metadata.MetadataServiceV2:2.0.0但 dubbo-go provider 实际只导出了 MetadataService V1,可能产生类似错误:
don't have this exporter, key: {app}/org.apache.dubbo.metadata.MetadataServiceV2:2.0.0因此,meta-v 不能只从业务协议或 endpoint 信息推断,必须来自实际 MetadataService 导出结果。
6.2 策略模型
引入统一的导出策略:
type MetadataServiceExportStrategy struct {
Protocol string
ExportV1 bool
ExportV2 bool
V1URL *common.URL
V2URL *common.URL
}建议规则:
| metadata-type | metadata-service-protocol | Export V1 | Export V2 | meta-v |
|---|---|---|---|---|
| local | dubbo | yes | no | 1.0.0 |
| local | tri | yes | yes | 2.0.0 |
| remote | any | no | no | not required |
| local | empty | follow default protocol resolution | follow default protocol resolution | derived from successful export |
V2-only metadata export 不是 dubbo-go 现有配置,不应在第一阶段引入。如果社区后续需要 V2-only 模式,应该作为单独的兼容性决策讨论。
硬性规则:
meta-v=2.0.0 only if MetadataServiceV2 is actually exported and reachable.实现上不能只根据 dubbo.metadata-service.url-params 推断 meta-v。应该在 MetadataService 导出完成后,根据导出策略解析版本:
func ResolveMetadataVersion(strategy MetadataServiceExportStrategy) string {
if strategy.ExportV2 && strategy.V2URL != nil {
return constant.MetadataServiceV2Version
}
if strategy.ExportV1 && strategy.V1URL != nil {
return constant.MetadataServiceV1Version
}
return ""
}如果某个协议导出路径目前无法返回 error,也应该只在 exporter 对象创建成功后记录 capability。V2 导出失败或被跳过时,绝不能发布 meta-v=2.0.0。
7. 配置设计
当前 ApplicationConfig 已经有类似字段:
MetadataType string
MetadataServicePort string
MetadataServiceProtocol string主要文件:
global/application_config.go
config/application_config.gometadata 初始化时应该确保 MetadataServiceProtocol 被传入 metadata options。
建议修改:
func initMetadata(rc *RootConfig) error {
opts := metadata.NewOptions(
metadata.WithAppName(rc.Application.Name),
metadata.WithMetadataType(rc.Application.MetadataType),
metadata.WithPort(getMetadataPort(rc)),
metadata.WithMetadataProtocol(rc.Application.MetadataServiceProtocol),
)
return opts.Init()
}第一阶段的最小改动是打通 MetadataServiceProtocol,并在该配置为空时保持当前默认行为。基于 rc.Protocols 自动推断协议可以后续再做,但必须配套明确的兼容性测试。
用户配置示例:
dubbo:
application:
name: demo-provider
metadata-type: local
metadata-service-protocol: tri
metadata-service-port: 20881
protocols:
tri:
name: tri
port: 50051
registries:
nacos:
protocol: nacos
address: 127.0.0.1:8848
registry-type: service主要更新文件:
config/metadata_config.go
metadata/options.go8. MetadataInfo 设计
8.1 增加线程安全
当前 metadata 使用全局 map,建议通过 manager 统一保护。
建议模型:
type MetadataManager struct {
mu sync.RWMutex
infos map[string]*info.MetadataInfo
}对外 API 保持兼容:
func GetMetadataInfo(registryId string) *info.MetadataInfo
func AddService(registryId string, url *common.URL)
func RemoveService(registryId string, url *common.URL)
func AddSubscribeURL(registryId string, url *common.URL)
func RemoveSubscribeURL(registryId string, url *common.URL)主要文件:
metadata/metadata.go8.2 Revision 计算
revision 计算应迁移到 MetadataInfo 内部。
建议 API:
func (m *MetadataInfo) CalAndGetRevision() string {
m.mu.Lock()
defer m.mu.Unlock()
if m.Revision != "" && !m.updated {
return m.Revision
}
if len(m.Services) == 0 {
m.Revision = "0"
m.updated = false
return m.Revision
}
m.Revision = m.calRevisionLocked()
m.updated = false
return m.Revision
}确定性计算方式:
func (m *MetadataInfo) calRevisionLocked() string {
keys := make([]string, 0, len(m.Services))
for key := range m.Services {
keys = append(keys, key)
}
sort.Strings(keys)
var builder strings.Builder
builder.WriteString(m.App)
for _, key := range keys {
builder.WriteString(m.Services[key].ToDescString())
}
return revision.Resolve(builder.String())
}ServiceInfo.ToDescString():
func (s *ServiceInfo) ToDescString() string {
return s.GetMatchKey() +
strconv.Itoa(s.Port) +
s.Path +
sortedParamsString(s.Params)
}只有稳定的服务语义应参与 revision。timestamp、进程本地地址,以及其他不会改变可调用服务行为的噪声字段必须排除。参与计算的字段应从现有 metadata IncludeKeys 出发,并与 Java Dubbo 的 MetadataInfo revision 行为对齐后再扩展。
该设计更接近 Java Dubbo 当前 MetadataInfo.calRevision() 的语义,即:
app + sorted service desc strings主要更新文件:
metadata/info/metadata_info.go
registry/servicediscovery/customizer/service_revision_customizer.go9. MetadataService V1 / V2 导出
9.1 MetadataService V1
服务名:
org.apache.dubbo.metadata.MetadataService版本:
1.0.0用途:
- 保留 MetadataService V1 作为 legacy 和迁移 fallback。
- 在 V2 不可用时支持 dubbo 协议下的 metadata service。
- 避免破坏仍依赖 V1 metadata 调用的现有 dubbo-go 用户。
Dubbo 2.7 兼容性不是本工作的主要设计目标。主要目标应是 Java Dubbo 3.3 和当前 dubbo-go develop。V1 应作为低成本 fallback 保留,但第一阶段不应增加 2.7 专属逻辑,也不应把 2.7 作为硬性验收要求。
9.2 MetadataService V2
服务名:
org.apache.dubbo.metadata.MetadataServiceV2版本:
2.0.0协议:
tri必需方法:
GetMetadataInfo
GetOpenAPIInfo第一阶段 GetOpenAPIInfo 可先返回空定义:
func (m *MetadataServiceV2) GetOpenAPIInfo(
ctx context.Context,
req *tripleapi.OpenAPIRequest,
) (*tripleapi.OpenAPIInfo, error) {
return &tripleapi.OpenAPIInfo{Definition: ""}, nil
}主要更新文件:
metadata/triple_api/proto/metadata_service_v2.proto
metadata/metadata_service.go10. ServiceInstance Metadata
provider 应向应用实例写入以下 metadata:
dubbo.metadata.storage-type = local | remote
dubbo.metadata.revision = {revision}
dubbo.subscribed-services.revision = {revision}
dubbo.metadata-service.url-params = {...}
dubbo.metadata-service.urls = [...]
dubbo.endpoints = [{"protocol":"tri","port":50051},{"protocol":"dubbo","port":20880}]
meta-v = 1.0.0 | 2.0.010.1 `dubbo.metadata-service.url-params`
示例:
{
"protocol": "tri",
"port": "20881",
"version": "2.0.0",
"release": "dubbo-golang-3.x.x"
}主要文件:
registry/servicediscovery/customizer/metadata_service_url_params_customizer.go该 JSON 描述的是 metadata service endpoint,而不是业务服务 endpoint。如果 V1 和 V2 都通过 triple 导出,该字段可以指向优先使用的 V2-capable endpoint,但 meta-v 的事实来源仍然是导出策略。
10.2 `meta-v`
meta-v 应来自实际导出策略:
func ResolveMetadataVersion(strategy MetadataServiceExportStrategy) string {
if strategy.ExportV2 {
return constant.MetadataServiceV2Version
}
if strategy.ExportV1 {
return constant.MetadataServiceV1Version
}
return ""
}主要文件:
registry/servicediscovery/customizer/metadata_service_version_customizer.go当前 customizer 通过 dubbo.metadata-service.url-params 推导 meta-v,这不够可靠。因为 URL params 可以显示 tri,但 V2 导出未必真的成功。customizer 应读取已记录的 metadata service 导出 capability。
10.3 `dubbo.endpoints`
dubbo.endpoints 应描述同一应用实例暴露的所有协议端口:
[
{"protocol": "tri", "port": 50051},
{"protocol": "dubbo", "port": 20880}
]consumer 应根据 ServiceInfo.Protocol 选择正确 endpoint。
主要文件:
registry/service_instance.go
registry/servicediscovery/customizer/11. Provider 生命周期
11.1 服务导出
预期流程:
ServiceOptions.Export()
-> build provider URL
-> registry protocol Register(url)
-> metadata.AddService(registryId, url)
-> serviceNameMapping.Map(url)主要文件:
server/action.go
registry/servicediscovery/service_discovery_registry.go
metadata/metadata.go11.2 注册应用实例
建议 provider 侧注册流程:
func (s *serviceDiscoveryRegistry) RegisterService() error {
registryId := s.url.GetParam(constant.RegistryIdKey, constant.DefaultKey)
metaInfo := metadata.GetMetadataInfo(registryId)
if metaInfo == nil {
return fmt.Errorf("metadata info not found, registry id = %s", registryId)
}
revision := metaInfo.CalAndGetRevision()
if metadata.GetMetadataType() == constant.RemoteMetadataStorageType {
report := metadata.GetMetadataReportByRegistry(registryId)
if report == nil {
return errors.New("metadata report not found")
}
if err := report.PublishAppMetadata(metaInfo.App, revision, metaInfo.Clone()); err != nil {
return err
}
}
instance := createInstance(metaInfo)
instance.GetMetadata()[constant.ExportedServicesRevisionPropertyName] = revision
return s.serviceDiscovery.Register(instance)
}重要语义变更:
One application process should register one application instance once.不要为每个导出的 service URL 注册一个应用实例。多协议和多端口应通过:
dubbo.endpoints表示。
该语义变更影响较大,应放在 metadata 协议和 meta-v 修复之后,并通过集成测试保护。
12. Consumer 生命周期
当前 consumer 流程应保留并增强:
ServiceInstancesChangedEvent
-> group instances by app + revision
-> get MetadataInfo from cache
-> if cache miss:
local: fetch from MetadataService RPC
remote: fetch from MetadataReport
-> MetadataInfo.Services
-> instance.ToURLs(serviceInfo)
-> notify registry directory主要文件:
registry/servicediscovery/service_instances_changed_listener_impl.go12.1 Metadata 获取
metadata 获取应继续基于 storage-type 判断:
if storageType == constant.RemoteMetadataStorageType {
metadataInfo, err = metadata.GetMetadataFromMetadataReport(revision, instance)
} else {
metadataInfo, err = metadata.GetMetadataFromRpc(revision, instance)
}主要文件:
metadata/client.go12.2 本地 RPC 获取 fallback
建议 fallback 顺序:
1. 如果 meta-v=2.0.0,先调用 MetadataServiceV2.GetMetadataInfo。
2. 如果 V2 失败且存在 V1 参数,则回退到 MetadataService V1。
3. 如果响应是 string,则按 JSON 解析以支持迁移兼容。
4. 如果某个实例失败,则尝试同一 app + revision 下的其他实例。
5. 如果全部失败,则跳过该 revision 并记录 warning,不能 panic。必需实现细节:
1. 如果 dubbo.metadata-service.url-params 缺失或非法,不能解引用 nil URL。
2. 标准路径优先使用 dubbo.metadata-service.url-params。
3. 如果 url-params 无法构造可用 URL,尝试 dubbo.metadata-service.urls。
4. V2 fallback 到 V1 时,重建 URL:
- interface/path = org.apache.dubbo.metadata.MetadataService
- version = 1.0.0
- method = getMetadataInfo
5. 只有返回非 nil 且包含服务信息的 MetadataInfo 后才写入缓存。12.3 Cache Key
当前缓存不能只使用 revision。
推荐 cache key:
{app}:{revision}原因:
不同应用可能生成相同 revision。
只使用 revision 会导致跨应用 metadata 缓存冲突。13. 兼容性矩阵
| Provider | Consumer | metadata-type | MetadataService | 预期 |
|---|---|---|---|---|
| dubbo-go | dubbo-go | local | V1/V2 | OK |
| dubbo-go | dubbo-go | remote | metadata report | OK |
| dubbo-go | Java Dubbo 3.3 | local + tri | V2 preferred, V1 fallback | OK |
| dubbo-go | Java Dubbo 3.3 | remote | metadata report | OK |
| Java Dubbo 3.3 | dubbo-go | local | V2 preferred, V1 fallback | OK |
| Java Dubbo 3.3 | dubbo-go | remote | metadata report | OK |
| dubbo-go | Java Dubbo 2.7 | local | V1 | Best effort / legacy fallback |
| Java Dubbo 2.7 | dubbo-go | local | V1 | Best effort / legacy fallback |
关键兼容性规则:
meta-v must match an actually exported MetadataService version.Java Dubbo 2.7 兼容性应视为 legacy 迁移支持,而不是主线目标。本设计不应围绕 Dubbo 2.7 的应用级服务发现机制优化,因为目标基准是 Dubbo 3.x 的 metadata 和服务发现行为。
14. 错误处理
metadata 是服务发现关键路径的一部分。一个异常 provider 实例不能导致 consumer 崩溃。
必需行为:
- Metadata RPC 失败返回 error,不能 panic。
- Metadata 反序列化失败时跳过当前实例。
- 如果一个实例失败,则尝试同一
app + revision下的其他实例。 - 空 metadata 不应写入缓存。
- 缺失 revision 时记录 warning 并跳过。
meta-v=2.0.0但 V2 调用失败时,应在可能的情况下回退到 V1。url-params非法时,应尽可能回退到dubbo.metadata-service.urls。
15. 指标和日志
建议指标:
metadata.push.rt
metadata.subscribe.rt
metadata.rpc.fetch.rt
metadata.report.fetch.rt
metadata.cache.hit
metadata.cache.miss
metadata.fetch.error建议日志:
[METADATA_REGISTER] metadata revision changed: old -> new, app: demo, services: 3
[METADATA_REGISTER] publish remote metadata app=demo revision=xxx
[METADATA_SERVICE] export V1 url=...
[METADATA_SERVICE] export V2 url=...
[METADATA_SUBSCRIBE] fetch metadata app=demo revision=xxx storage=local
[METADATA_SUBSCRIBE] cache hit app=demo revision=xxx
[METADATA_SUBSCRIBE] fallback V2 -> V1 app=demo revision=xxx16. 测试计划
16.1 单元测试
MetadataInfo
AddService正确更新 services。RemoveService正确更新 services。- 相同服务用不同插入顺序生成相同 revision。
- 参数变化会导致 revision 变化。
- 方法级参数变化会导致 revision 变化。
- 空 services 返回 revision
"0"。
MetadataService Strategy
metadata-service-protocol=dubbo只导出 V1。metadata-service-protocol=tri导出 V1 + V2。metadata-type=remote不导出本地 MetadataService。meta-v匹配已记录的成功导出结果。- V2 导出失败时绝不生成
meta-v=2.0.0。
Proto Conversion
MetadataInfo -> MetadataInfoV2不丢字段。MetadataInfoV2 -> MetadataInfo不丢字段。ServiceInfo.Port、Path和Params正确转换。- V2 转换保留
ServiceInfo.Port。
Metadata Cache
- 相同 app 和相同 revision 复用 metadata。
- 不同 app 即使 revision 相同,也不共享 metadata cache entry。
- nil 或空 metadata 不写入缓存。
16.2 集成测试
Local Metadata
Go provider:
metadata-type=local
metadata-service-protocol=tri
Go consumer:
service discovery subscribe
fetch MetadataServiceV2
generate provider URL
invoke successfullyRemote Metadata
Go provider:
metadata-type=remote
metadata-report=nacos
Go consumer:
service discovery subscribe
fetch metadata from metadata report
invoke successfullyJava Dubbo 3.3 Compatibility
Java Dubbo 3.3 consumer -> Go provider V2
Java Dubbo 3.3 consumer -> Go provider V1 fallback
Go consumer -> Java Dubbo 3.3 provider V2
Go consumer -> Java Dubbo 3.3 provider V1 fallbackDubbo 2.7 应作为 best-effort legacy 场景,在实际可行时由 V1 fallback 覆盖,但不应阻塞第一阶段验收。
Multi-provider
same application, 3 provider instances:
port=20000
port=20001
port=20002
consumer should see 3 provider URLs.Multi-protocol
same provider exposes:
tri:50051
dubbo:20880
instance metadata should contain:
dubbo.endpoints=[tri, dubbo]
consumer should generate URLs according to ServiceInfo.Protocol.17. 实施计划
PR 1:修复 MetadataService Protocol 配置
范围:
config/metadata_config.go
metadata/options.go任务:
- 将
Application.MetadataServiceProtocol传递给 metadata options。 - 增加 metadata export strategy。
- 确保
metadata-type=remote不导出本地 MetadataService。 - 当
metadata-service-protocol为空时保持当前默认行为。 - 增加单元测试。
PR 2:对齐 MetadataService V2 Proto
范围:
metadata/triple_api/proto/metadata_service_v2.proto
metadata/metadata_service.go任务:
- 增加
GetOpenAPIInfo。 - 增加
OpenAPIRequest。 - 增加
OpenAPIInfo。 - 增加
OpenAPIFormat。 - 实现返回空定义的
GetOpenAPIInfo。 - 重新生成 generated code。
- 更新 service descriptor。
PR 3:修复 `meta-v` 生成
范围:
registry/servicediscovery/customizer/metadata_service_version_customizer.go
metadata/metadata_service.go任务:
- 根据实际 export strategy 生成
meta-v。 - 确保只有 V2 实际导出时才生成
meta-v=2.0.0。 - 停止只根据
dubbo.metadata-service.url-params推导meta-v。 - 增加 Java Dubbo 3.3 consumer 兼容性测试。
PR 4:将 Revision 计算迁移到 MetadataInfo
范围:
metadata/info/metadata_info.go
registry/servicediscovery/customizer/service_revision_customizer.go任务:
- 增加
CalAndGetRevision()。 - 增加
ServiceInfo.ToDescString()。 - 替换 customizer 中的 revision 逻辑。
- 增加确定性 revision 测试。
PR 5:增强 Consumer Metadata Fetch
范围:
metadata/client.go
registry/servicediscovery/service_instances_changed_listener_impl.go任务:
- 将 cache key 改成
{app}:{revision}。 - 增加 V2 -> V1 fallback。
- 避免 nil metadata 导致 panic。
- 当
url-params非法时 fallback 到dubbo.metadata-service.urls。 - 不缓存 nil 或空 metadata。
- 当某个实例失败时尝试同 revision 下的其他实例。
- 增加集成测试。
PR 6:收敛应用级实例语义
范围:
registry/servicediscovery/service_discovery_registry.go
registry/service_instance.go
registry/servicediscovery/customizer/任务:
- 用回归测试保护
develop分支已有的多 provider URL 生成行为。 - 确认 provider 注册应继续保持“每个导出 URL 一个实例”,还是收敛为“每个进程一个应用实例”。
- 如果改为每个进程一个应用实例,则使用
dubbo.endpoints表达多协议端口。 - 在改变注册语义前,先补齐 Nacos 多 provider 和多协议集成测试。
- 不要把注册语义变更和 metadata protocol /
meta-v修复放在同一个 PR 中。
18. 验收标准
- 配置
metadata-service-protocol: tri时,provider 实际导出MetadataServiceV2。 - 当
meta-v=2.0.0时,Java Dubbo 3.3 consumer 可以调用 dubbo-go provider 的MetadataServiceV2.GetMetadataInfo。 - 配置
metadata-service-protocol: dubbo时,不能写出meta-v=2.0.0。 metadata-type=remote时,provider 不依赖本地 MetadataService,consumer 能从 metadata report 获取 metadata。- 多 provider 场景下,consumer directory 能看到所有 provider URL,并且有回归测试保护。
- 多协议场景下,consumer 能根据
ServiceInfo.Protocol选择正确 endpoint。 - metadata 拉取失败不会导致 panic。
- 相同
{app}:{revision}不会重复拉取 metadata。 - revision 变化会触发 metadata 刷新。
- Go / Java Dubbo 3.3 双向 metadata 兼容性测试通过。
- 不同应用即使 revision 相同,也不会共享 metadata 缓存。
19. 推荐顺序
推荐顺序:
- 修复
metadata-service-protocol传递。 - 修复基于实际导出结果的
meta-v生成。 - 对齐 MetadataServiceV2 proto 与 Java Dubbo。
- 增加小范围兼容性测试。
- 然后再继续 revision 计算、cache key 和注册语义收敛。
这些改动足够小,同时又能解决真实的 Java / Go 互操作问题。
20. 总结
dubbo-go 不需要从零重建 metadata 系统。
正确方向是增强现有架构:
MetadataInfo
+ MetadataService V1/V2
+ ServiceInstance metadata
+ MetadataReport
+ Consumer metadata fetch by revision最重要的设计规则是:
Service instance metadata must describe the actual provider capability.具体来说:
meta-v=2.0.0 means MetadataServiceV2 is actually exported and reachable.只要这一点成立,Java Dubbo 与 dubbo-go 的 metadata 互操作就会更可预测。
