Spring解析并注册Dubbo consumer端过程详解

这里debug的代码用的是github上dubbo项目的dubbo-demo里的dubbo-demo-xml下的代码。这里以默认的dubbo通信协议为debug的代码,由于这一篇都是dubbo框架内部实现,所以建议先看下dubbo官网上的一篇讲dubbo的设计原则的文章,有助于理解代码,这里先贴出dubbo官网上的架构图,然后我们在看代码时,比对架构图一起分析:
在这里插入图片描述

下面是架构图中的各层说明:

  • 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

demo里的consumer端的xml配置:

<beans 
       // xmlns:xsi是xsi标签命名空间
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       // xmlns:dubbo是dubbo标签的命名空间
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       // 当前那xml文件默认的命名空间
       xmlns="http://www.springframework.org/schema/beans"
       // xsi:schemaLocation 配置了每个命名空间对应里配置规范,用来做格式校验
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="demo-consumer"/>

    <dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="6000"/>
    
    <!--    协议配置-->
    <dubbo:protocol name="dubbo"/>
    
    <!--    consumer配置-->
    <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService" timeout="6000" />

</beans>
复制代码

1、将dubbo:reference配置解析成BeanDefinition

Dubbo的consumer配置是<dubbo:reference>,在《spring解析dubbo标签配置过程》中可以看到这个配置被解析成BeanDefinition类的过程,时序图如下:
在这里插入图片描述

2、通过BeanDefinition实例化ReferenceConfig对象

BeanDefinition的BeanClass属性值为org.apache.dubbo.config.spring.ReferenceConfig.class类,该类实现了FactoryBean接口(FactoryBean接口可以说就是为了这种通过动态代理技术生成bean的场景设计的,Spring在注入时,会注入该对象的getObject()方法返回真正的实例)。getBean()详细逻辑见这里,debug截图如下:
在这里插入图片描述

3、依赖注入时,触发ReferenceConfig这个FactoryBeangetObject()调用,返回代理类

Spring在依赖注入时也会调用getBean(String beanName)方法来获取已经实例化的bean,通过beanName会获取第二步中实例化的ReferenceConfig对象,而该对象是BeanFactory,所以最终会调用getObject()方法,debug截图如下:
在这里插入图片描述

getObject()截图如下:
在这里插入图片描述

getObject()代码如下,我这里省略了部分分支代码:

public Object getObject() {
        return this.get();
}

public synchronized T get() {
    ... 
    this.init();
    ...
    return this.ref;
}

public synchronized void init() {
    // 初始化dubbo启动类
    if (this.bootstrap == null) {
        this.bootstrap = DubboBootstrap.getInstance();
        this.bootstrap.init();
    }
    
    ...
    
    // map保存所有创建consumer代理类的相关配置信息
    Map<String, String> map = new HashMap();
    map.put("side", "consumer");
    // 往map中插入dubbo版本号、时间戳、进程pid运行时信息
    ReferenceConfigBase.appendRuntimeParameters(map);
    ...
    
    // 传入map,创建代理类
    this.ref = this.createProxy(map);
    ...
}
  
private T createProxy(Map<String, String> map) {
    ...
    // 检查注册中心配置,并根据注册中心配置生成注册中心的URL
    this.checkRegistry();
    List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
    if (CollectionUtils.isNotEmpty(us)) {
        for(Iterator var3 = us.iterator(); var3.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {
            u = (URL)var3.next();
            monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
            if (monitorUrl != null) {
                map.put("monitor", URL.encode(monitorUrl.toFullString()));
            }
        }
    }
    ...
    // 构建invoker链
    this.invoker = REF_PROTOCOL.refer(this.interfaceClass, (URL)this.urls.get(0));
    ...        
    // 通过ProxyFactory创建代理
    return PROXY_FACTORY.getProxy(this.invoker);
}
复制代码

下面是结合dubbo架构图画的主要步骤的时序图:
在这里插入图片描述

3.1 初始化Dubbo启动类DubboBootstrap

这个DubboBootstrap是dubbo的启动类,类似于springbootSpringApplication类,存储了整个dubbo环节的各种配置信息,负责provider的服务暴露及consumer的服务订阅、维护dubbo容器的生命周期,这里先跳过,继续研究我们的consumer实例化过程

3.2 构建注册中心对象

代码在createProxy(Map<String, String> map)方法里,核心代码如下

this.checkRegistry();
List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
// 获取注册中心的URL,并将当前consumer配置的元数据信息放到URL的Parameter属性里
    for(Iterator var3 = us.iterator(); var3.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {
        ...
    }
}
复制代码

<dubbo:registry>配置中心对应的bean是RegistryConfig.class,解析过程和<dubbo:reference>一样,被解析出来的配置都会放到一个全局的ConfigManager中,这里会从这个ConfigManager中读取注册中心配置。dubbo里的常见的配置都会缓存起来,在《spring的xml文件里dubbo标签解析过程》里可以看到负责解析dubbo自定义标签的是DubboNamespaceHandler,每种配置对应的实体类如下:
在这里插入图片描述
这些类都继承了AbstractConfig类,这个抽象类有个被@PostConstruct注解标注的addIntoConfigManager()方法(该注解会告诉Spring容器在实例化该对象后,会执行注解标注的方法),会将当前对象缓存到一个全局的ConfigManager对象中:

    @PostConstruct
    public void addIntoConfigManager() {
        ApplicationModel.getConfigManager().addConfig(this);
    }
复制代码

Url可以说是dubbo主链路里分量非常重的一个参数封装类,在主链路里封装参数一路传递,这里就是讲注册中心的元数据与consumer的元数据封装到一个URL中,传递到下游的方法里,主要存储一些元数据信息,这里就封装了注册中心地址、consumer的接口、方法等信息,debug截图如下:
在这里插入图片描述

private static final Protocol REF_PROTOCOL = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

this.invoker = REF_PROTOCOL.refer(this.interfaceClass, (URL)this.urls.get(0));
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享