
深入解析 Dubbo 3.0 服务端暴露全流程
2021-09-06 09:46:26随着云原生时代的到来,Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生。正因如此,Dubbo 3.0 为了能够更好的适配云原生,将原来的接口级服务发现机制演进为应用级服务发现机制。
背景
随着云原生时代的到来,Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生。正因如此,Dubbo 3.0 为了能够更好的适配云原生,将原来的接口级服务发现机制演进为应用级服务发现机制。
基于应用级服务发现机制,Dubbo 3.0 能大幅降低框架带来的额外资源消耗,大幅提升资源利用率,主要体现在:
单机常驻内存下降 75%
能支持的集群实例规模以百万计的集群注册中心总体数据量下降超 90%目前关于 Dubbo 服务端暴露流程的技术文章很多,但是都是基于 Dubbo 接口级服务发现机制来解读的。在 Dubbo 3.0 的应用级服务发现机制下,服务端暴露流程与之前有很大的变化,本文希望可以通过 对Dubbo 3.0 源码理解来解析服务端暴露全流程。
什么是应用级服务发现
简单来说,以前 Dubbo 是将接口的信息全部注册到注册中心,而一个应用实例一般会存在多个接口,这样一来注册的数据量就要大很多,而且有冗余。应用级服务发现的机制是同一个应用实例仅在注册中心注册一条数据,这种机制主要解决以下几个问题:
对齐主流微服务模型,如:Spring Cloud支持 Kubernetes native service,Kubernetes 中维护调度的服务都是基于应用实例级,不支持接口级减少注册中心数据存储能力,降低了地址变更推送的压力假设应用 dubbo-application 部署了 3 个实例(instance1, instance2, instance3),并且对外提供了 3 个接口(sayHello, echo, getVersion)分别设置了不同的超时时间。在接口级和应用级服务发现机制下,注册到注册中心的数据是截然不同的。如下图所示:
接口级服务发现机制下注册中心中的数据
应用级服务发现机制下注册中心中的数据
通过对比我们可以发现,采用应用级服务发现机制确实使注册中心中的数据量减少了很多,那些原有的接口级的数据存储在元数据中心中。
服务端暴露全流程
引入应用级服务发现机制以后,Dubbo 3.0 服务端暴露全流程和之前有很大的区别。暴露服务端全流程的核心代码在 DubboBootstrap#doStart 中,具体如下:
假设以 Zookeeper 作为注册中,对外暴露 Triple 协议的服务为例,服务端暴露全流程时序图如下:
我们可以看到,整个的暴露流程还是挺复杂的,一共可以分为四个部分:

暴露 injvm 协议的服务注册 service-discovery-registry 协议暴露 Triple 协议的服务并注册 registry 协议暴露 MetadataService 服务下面会分别从这四个部分对服务暴露全流程进行详细讲解。
1、暴露 injvm 协议的服务
injvm 协议的服务是暴露在本地的,主要原因是在一个应用上往往既有 Service(暴露服务)又有 Reference(服务引用)的情况存在,并且 Reference 引用的服务就是在该应用上暴露的 Service。为了支持这种使用场景,Dubbo 提供了 injvm 协议,将 Service 暴露在本地,Reference 就可以不需要走网络直接在本地调用 Service。
整体时序图
由于这部分内容在之前的接口级服务发现机制中是类似的,所以相关的核心代码就不在这里展开讨论了。
2、注册 service-discovery-registry 协议
注册 service-discovery-registry 协议的核心目的是为了注册与服务相关的元数据,默认情况下元数据通过 InMemoryWritableMetadataService 将数据存储在本地内存和本地文件。
整体时序图
核心代码在 ServiceConfig#exportRemote 中,具体如下:
注册 service-discovery-registry 协议的入口
invoker 中包装 Metadata
核心代码在 ServiceConfig#doExportUrl 中,具体如下:
通过 RegistryProtocol 将 Invoker 转化成 Exporter
核心代码在 ProtocolListenerWrapper#export 中,具体如下:
RegistryProtocol 将 Invoker 转化成 Exporter 的核心流程
核心代码在 RegistryProtocol#export 中,具体如下:
暴露 Triple 协议的服务
核心代码在 RegistryProtocol#doLocalExport 中,具体如下:
注册service-discovery-registry协议
核心代码在 ServiceDiscoveryRegistry#register和ServiceDiscoveryRegistry#doRegister 中,具体如下:
1、ServiceDiscoveryRegistry#register
2、ServiceDiscoveryRegistry#doRegister
注册元数据
核心代码在 InMemoryWritableMetadataService#exportURL 中,具体如下:
发布 onRegister 事件
核心代码在 ListenerRegistryWrapper#register 中,具体如下:
发布服务注册事件
核心代码在 RegistryProtocol#notifyExport 中,具体如下:
我们可以看出注册 service-discovery-registry 协议的核心目的是为了将服务的接口相关的信息存储在内存中。从兼容性和平滑迁移两方面来考虑,社区在实现的时候采取复用 ServiceConfig 的暴露流程的方式。
3、暴露Triple协议服务并注册registry协议
暴露 Triple 协议的服务并注册 registry 协议是 Dubbo 3.0 服务暴露的核心流程,一共分为两部分:
暴露 Triple 协议的服务
注册 registry 协议由于暴露 Triple 协议服务的流程和暴露 Injvm 协议服务的流程是一致的,所以不再赘述。注册 registry 协议的过程仅仅注册了应用实例相关的信息,也就是之前提到的应用级服务发现机制。
整体时序图

通过 InterfaceCompatibleRegistryProtocol 将 Invoker 转化成 Exporter
核心代码在 ProtocolListenerWrapper#export 中,具体如下:
RegistryProtocol 将 Invoker 转化成 Exporter 的核心流程
核心代码在 RegistryProtocol#export 中,具体如下:
注册 registry 协议
核心代码在 FailbackRegistry#register 和 ServiceDiscoveryRegistry#doRegister 中(ZookeeperRegistry 继承 FailbackRegistry)中,具体如下:
1、FailbackRegistry#register
2、ServiceDiscoveryRegistry#doRegister
订阅地址变更
核心代码在 FailbackRegistry#subscribe 和 ZookeeperRegistry#doSubscribe 中,具体如下:
1、FailbackRegistry#subscribe
2、ZookeeperRegistry#doSubscribe
建立暴露的 Triple 协议服务与 Metadata 之间的联系核心代码在 ServiceConfig#exportUrl、MetadataUtils#publishServiceDefinition、InMemoryWritableMetadataService#publishServiceDefinition、RemoteMetadataServiceImpl#publishServiceDefinition 和 MetadataReport#storeProviderMetadata 中,具体如下:
1、ServiceConfig#exportUrl
2、MetadataUtils#publishServiceDefinition
3、InMemoryWritableMetadataService#publishServiceDefinition
4、RemoteMetadataServiceImpl#publishServiceDefinition
5、AbstractMetadataReport#storeProviderMetadata
建立 Triple 协议服务与 MetadataReport 服务之间的关系核心代码在 ServiceConfig#exported、MetadataServiceNameMapping#map 和 ZookeeperMetadataReport#registerServiceAppMapping 中,具体如下:
1、ServiceConfig#exported
2、MetadataServiceNameMapping#map
3、ZookeeperMetadataReport#registerServiceAppMapping
到这里,暴露Triple协议的服务并注册 registry 协议的流程就结束了。主要是将以前接口级服务发现机制中注册到注册中心中的数据(应用实例数据+服务接口数据)拆分出来了。注册 registry 协议部分将应用实例数据注册到注册中心,在 Exporter 暴露完以后通过调用 MetadataUtils#publishServiceDefinition 将服务接口数据注册到元数据中心。
4、暴露MetadataService服务
MetadataService 主要是对 Consumer 侧提供一个可以获取元数据的 API,暴露流程是复用了 Triple 协议的服务暴露流程
整体时序图
暴露 MetadataService 的入口
核心代码在 DubboBootstrap#exportMetadataService 中,具体如下:
暴露 MetadataService
核心代码在 ConfigurableMetadataServiceExporter#export 中,具体如下:
由于暴露 MetadataService 的流程是复用前面提到的暴露 Triple 协议服务的流程,整个过程有少许地方会不同,这些不同之处在上面的代码中都已经标明,所以就不再赘述了。
注册 ServiceInstance 实例
注册 ServiceInstance 的目的是为了定时更新 Metadata,当有更新的时候就会通过 MetadataReport 来更新版本号让 Consumer 端感知到。
核心代码在 DubboBootstrap#registerServiceInstance 和 DubboBootstrap#doRegisterServiceInstance 中,具体如下:
DubboBootstrap#doRegisterServiceInstance
通过上面的分析,我们可以很容易知道
ServiceInstance 是中包含 MetadataMetadata 是存储在 InMemoryWritableMetadataService 中的元数据,占用的是本地内存空间InMemoryWritableMetadataService 用来更新 MetadataServiceInstance 是存储在远端元数据注册中心中的数据结构RemoteMetadataServiceImpl 会调用 metadataReport 将 ServiceInstance 数据更新到远端元数据注册中心
总结
通过对 Dubbo 3.0 服务端暴露全流程的解析可以看出,尽管应用级服务发现机制的实现要复杂很多,但是 Dubbo 3.0 为了能够让使用者平滑迁移,兼容了 2.7.x 的版本,所以在设计的时候很多地方都尽可能复用之前的流程。
从最近 Dubbo 3.0 发布的 Benchmark 数据来看,Dubbo 3.0 的性能和资源利用上确实提升了不少。Dubbo 3.0 在拥抱云原生的道路上还有很长的一段路要走,社区正在对 Dubbo 3.0 中核心流程进行梳理和优化,后续计划支持多实例应用部署,希望有兴趣见证 Dubbo 云原生之路的同学可以积极参与社区贡献!
发表评论