【dubbo-go 源码解析】Dubbo-go 服务发现如何改成应用级?|Go主题月

608 阅读3分钟

前文(Dubbo-go 中服务如何自动被发现?)已经提到过 Dubbo-go 从服务级发现到应用级发现改造时的三大关键问题,此三大问题的解决方案,可以总结成:应用级发现模型、区分应用与服务信息(元数据)、元数据与应用级发现模型的关联。

应用级服务发现模型

应用级发现模型改造之后,注册中心数据变成为应用作为维度(样例1)。改造后的数据,Consumer 端需要怎么改造才能使用?

`"application1": [`
 `{"name":"instance1", "ip":"127.0.0.1", "metadata":{}},`
 `{"name":"instance2", "ip":"127.0.0.2", "metadata":{}},`
 `{"name":"instanceN", "ip":"127.0.0.3", "metadata":{}}`
`]`

样例 1

之前文章已经提到过 Consumer 的启动入口是ReferenceConfig.Refer(),而 Consumer 启动后随即内建元数据中心,并订阅注册中心获取已存在的实例列表。但是想找到如何订阅注册中心的入口是比较不容易。

前文已经提到过,Dubbo-go 的 Consumer 默认情况下,会通过以下 URL 进行订阅注册中心。在应用级服务发现的情况下呢?Consumer 会得到以下 URL (样例 2),并通过该 URL 订阅注册中心。

`registry://:?group=&metadata=default&name_mapping=in-memory&registry=service-discovery&registry.label=true&registry.preferred=false&registry.role=0&registry.timeout=5s&registry.ttl=10m&registry.weight=0&registry.zone=&service_discovery=zk-dis1&simplified=false`

样例 2

URL 中协议为 registry ,而 Protocol 接口则是为解析协议的接口,那这两个事情是否有关联呢?先看看 Protocol 的类图:

图 1

从 Protocol 的类图(图 1)得知,RegistryProtocol 是处理注册(registry)协议的类。而其中包含的 Refer 方法则是我们需要找的订阅注册中心的方法。接下来,就深入 RegistryProtocol.Refer 的内部:

`func (proto *registryProtocol) Refer(url *common.URL) protocol.Invoker {`
 `...`
 `if registryUrl.Protocol == constant.REGISTRY_PROTOCOL {`
 `registryUrl.Protocol = registryUrl.GetParam(constant.REGISTRY_KEY, "")`
 `}`
 `...`
`}`

样例 3

从上述代码得知,如果是 registry 协议时,则将其中 param 中的 registry 参数拿出,替换 URL 中的协议。最终协议会转成以下 URL(样例 4),使用 service-discovery 协议,进行应用级服务发现。

`service-discovery://:?group=&metadata=default&name_mapping=in-memory&registry=service-discovery&registry.label=true&registry.preferred=false&registry.role=0&registry.timeout=5s&registry.ttl=10m&registry.weight=0&registry.zone=&service_discovery=zk-dis1&simplified=false`

样例 4

而转换成应用级服务发现之后,又如何得知使用哪一种注册中心?

可以仔细观察以上 URL(样例 4),能发现其中有一个参数:service_discovery=zk-dis1。从之前的文章中,已经得知,该 URL 是通过配置文件中的配置项,转换得来(配置文件 -> URL),那 service_discovery=zk-dis1 是否在配置文件中有对应的配置?在配置文件中,可发现以下配置(样例 5)。

`remote:`
 `zk1:`
 `address: "127.0.0.1:2181"`
 `timeout: "5s"`
`...`
`service_discovery:`
 `zk-dis1:`
 `protocol: "zookeeper"`
 `remote_ref: "zk1"`

样例 5

然而,配置中 remote.zk1 在加载配置文件时,已经将构造函数放进缓存。在此时,调用 reg = getRegistry(registryUrl) 时(见样例 6),获得的是其构造函数,并将其用于后续对象初始化。

`var reg registry.Registry`
`if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded { <<< 启动时,调用时为空`
 `reg = getRegistry(registryUrl) <<< 通过 protocol 获取对应注册中心连接初始化方法。`
 `proto.registries.Store(registryUrl.Key(), reg)`
`} else {`
 `reg = regI.(registry.Registry)`
`}`

样例 6

Dubbo-go 最终会将 URL (样例 4 )转化成用于注册中心信息。从注册中心将 Provider 信息拉取下来之后,匹配 Provider 的信息。只有应用的信息,而 Dubbo-go 是基于方法发起的 RPC ,那方法在服务(接口)中,所以原来在服务级发现模型中,包含的方法名、方法参数等附加信息如何获得也就成为了关键。所以在 Dubbo-go 中将这部分信息归类成元数据,而存放元数据信息的地方,则叫元数据中心。接下来,看看元数据是怎么实现。

欢迎加入社区

在公众号【部长技术之路】后台回复关键字【dubbogo】加入社区。