浅谈Dubbo的注册发现流程

本文详细介绍了Dubbo的服务注册发现流程,包括其组件结构、初始化过程、生产者暴露服务和消费者引用服务的步骤。重点解析了Dubbo的源码,涉及到ServiceConfig、ReferenceConfig、Invoker和Exporter等关键概念,探讨了有无注册中心的情况。同时,文章讨论了生产者停止时的情况以及可能遇到的问题,最后进行了总结,强调了Dubbo设计的精妙之处。
摘要由CSDN通过智能技术生成

前言

 

笔者从接触分布式开始用的一直是Dubbo和Nacos作为分布式治理的框架,而之前一直没有研究过Dubbo他到底是如何进行服务注册和暴露的,很久之前就想写一篇关于Dubbo的文章,奈何一直没时间去看Dubbo源码,这几天研究了一下,所以在此分享一下自己的看法,如果有不同的见解欢迎大家一起讨论

环境

  1. 从github拉的代码开源网站
  2. 本文使用的是2.6.x分支(3.0太新还没去看,不过总体还是基于2.6-7版本换了个协议,详情看dubbo官网)
  3. openjdk8

dubbo的组成结构

dubbo的整体设计

说明:

  • 蓝色的为服务消费者所使用的接口,绿色的为服务提供者所使用的的接口,中轴线则是两者都使用到的接口或实现类。
  • Dubbo的整体设计可分为10层,各层均为单向依赖,而每一次都可以剥离上层被复用,其中Sevice层和Config层为API层,其他各层均为SPI。也就是拓展性非常好
  • 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
  • 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。

各层说明

  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

模块分包说明

  • dubbo-common 公共逻辑模块:包括 Util 类和通用模型。
  • dubbo-remoting 远程通讯模块:相当于 Dubbo 协议的实现,如果 RPC 用 RMI协议则不需要使用此包。此模块包含了10层中的transport层和exchange层为rpc调用的基础模块。
  • dubbo-rpc 远程调用模块:抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。此模块包含了10层中的protocol和proxy层。接口暴露和代理生成都在这一层。
  • dubbo-cluster 集群模块:将多个服务提供方伪装为一个提供方,包括:负载均衡, 容错,路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。此模块为10层中的cluster层
  • dubbo-registry 注册中心模块:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
  • dubbo-monitor 监控模块:统计服务调用次数,调用时间的,调用链跟踪的服务。此层为10层中的monitor层
  • dubbo-config 配置模块:是 Dubbo 对外的 API,用户通过 Config 使用Dubbo,隐藏 Dubbo 所有细节。此层包括了10层中的config层
  • dubbo-container 容器模块:是一个 Standlone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。
  • dubbo-serialization:rpc调用的序列化方式(目前有5种fastjson、fst、hessian2、jdk、kryo)默认为hessian2,此模块为10层中的serialize层

至此,Dubbo的所有源码模块和设计层数全部一一对应上。
由这些信息其实就可以知道Dubbo在设计的时候模块分层还是很清楚的。

项目启动初始化过程细节(生产者和消费者)

解析服务

基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler。所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。然后将 URL 传给 协议扩展点,基于扩展点的 扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。

暴露服务

  1. 在没有注册中心,直接暴露提供者的情况下 1.ServiceConfig 解析出的 URL 的格式为: dubbo://service-host/com.foo.FooService?version=1.0.0。基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocol的 export() 方法,打开服务端口。

  2. 在有注册中心,需要注册提供者地址的情况下 2,ServiceConfig 解析出的 URL 的格式为: registry://registry-host/org.apache.dubbo.registry.RegistryService?export=URL.encode("dubbo://service-host/com.foo.FooService?version=1.0.0"),基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 export() 方法,将 export 参数中的提供者 URL,先注册到注册中心。再重新传给 Protocol 扩展点进行暴露: dubbo://service-host/com.foo.FooService?version=1.0.0,然后基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 export() 方法,打开服务端口。

引用服务

  1. 在没有注册中心,直连提供者的情况下 3,ReferenceConfig 解析出的 URL 的格式为:dubbo://service-host/com.foo.FooService?version=1.0.0。基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocol 的 refer() 方法,返回提供者引用。

  2. 从注册中心发现引用服务:

在有注册中心,通过注册中心发现提供者地址的情况下 4,ReferenceConfig 解析出的 URL 的格式为: registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")。基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 refer() 方法,基于 refer 参数中的条件,查询提供者 URL,如: dubbo://service-host/com.foo.FooService?version=1.0.0。基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 refer() 方法,得到提供者引用。然后 RegistryProtocol 将多个提供者引用,通过 Cluster 扩展点,伪装成单个提供者引用返回。

项目启动初始化细节总结

不管生产者还是消费者,不管是否有注册中心,终究离不开Protocol这个接口。也就是说从生产者和消费者启动,他都会有Protocol产生,而当没有注册中心的时候,那么按上面所说的可以剔除上层并且复用,就会使用DubboProtocol实现类进行服务的暴露和引用。当有注册中心的时候根据Dubbo的自适应机制就会变成RegistryProtocol作为实现类,进行服务的暴露和引用。所以说Protocol的指责是对服务进行暴露和引用,而他的具体实现就要看使用Dubbo时的环境。
如图:

    Protocol.java
    /**
     * 用于远程调用的暴露接口,也就是Invoker转化成Exporter的接口
     * 1.协议在收到请求后要记录请求源地址RpcContext.getContext().setRemoteAddress();
     * 2.export()必须是幂等的,即导出同一个URL时调用一次和调用两次没有区别
     * 3.Invoker 实例是框架传入的,协议不用管
     * @param <T>     Service type 接口类型
     * @param invoker Service invoker 接口类型转成URL再转换成的Invoker
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    /**
     * 引用远程服务
     * 1.当用户调用从`refer()`调用返回的`Invoker`对象的`invoke()`方法时,协议需要对应执行`Invoker`对象的`invoke()`方法 
     * 2. 协议的责任是实现从 `refer()` 返回的 `Invoker`。 一般来说,协议在 `Invoker` 实现中发送远程请求。 
     * 3、当URL中设置了check=false时,实现一定不要抛出异常,而是在连接失败时尝试恢复。
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
复制代码

生产者暴露服务的过程

如上图: 首先ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程。Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程,上图中的红色部分。下面我们以 Dubbo 和 RMI 这两种典型协议的实现来进行说明:Dubbo 的实现 Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。

消费者引用服务的过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值