dubbo源码三:消费端启动过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhuqiuhui/article/details/84075521


服务端配置文件

<dubbo:application name="demo-provider"/>
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<dubbo:protocol name="dubbo" port="20880"/>

<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>

消费端配置文件

<dubbo:application name="demo-consumer"/>
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>

以上配置是 dubbo 源码中的 dubbo-demo 模块,直接看源码主流程:
Debug-1:
在这里插入图片描述
Debug-2:
在这里插入图片描述
Debug-3:
在这里插入图片描述
Debug-3处是调用的 init 方法来创建服务的代理,看创建代理方法:
Debug-4:
在这里插入图片描述
Debug-4 中首先检查配置是否引用的本地服务injvm(本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链)
在这里插入图片描述

顺便贴出 urls.get(0) 的内容:

registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=48937&qos.port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D48937%26qos.port%3D33333%26register.ip%3D172.17.13.52%26side%3Dconsumer%26timestamp%3D1542026031755®istry=multicast×tamp=1542026724226

接着走下去,
在这里插入图片描述
所以以上就分成两个步骤:

  1. 引用远程服务
  2. 创建服务代理
    介绍上面两个步骤之前,先看 ReferenceConfig 类初始化了三个属性,即如下,
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

private static final ProxyFactory proxyFactory=ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

一、引用远程服务

这里调用主干是:

invoker = refprotocol.refer(interfaceClass, urls.get(0));

interfaceClass:interface org.apache.dubbo.demo.DemoService
urls.get(0):registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=48937&qos.port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D48937%26qos.port%3D33333%26register.ip%3D172.17.13.52%26side%3Dconsumer%26timestamp%3D1542026031755®istry=multicast×tamp=1542026724226

调用 refprotocol.refer ,2.5.0 dubbo版本调用顺序依次是:ProtocolListenerWrapper -> ProtocolFilterWrapper -> RegistryProtocol (2.6.4 dubbo版本调用顺序依次是:ProtocolFilterWrapper -> QosProtocolWrapper -> ProtocolListenerWrapper -> RegistryProtocol),中间的顺序关系不大,因为最终是执行 RegistryProtocol ,这里直接看 RegistryProtocol 的 refer 方法,整体分成以下几步:

  • 获取注册中心
  • 消费端注册服务到注册中心
  • 创建RegistryDirectory,并订阅服务提供者
    在这里插入图片描述

2.1 获取注册中心

主代码:
Debug-5:
在这里插入图片描述
其中 url 值是:

multicast://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=48937&qos.port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D48937%26qos.port%3D33333%26register.ip%3D172.17.13.52%26side%3Dconsumer%26timestamp%3D1542026031755×tamp=1542026724226

registryFactory 的适配类 RegistryFactory$Adaptive 是

package com.alibaba.dubbo.registry;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class RegistryFactory$Adpative implements com.alibaba.dubbo.registry.RegistryFactory {

    public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) 
            throw new IllegalArgumentException("url == null");
        
        com.alibaba.dubbo.common.URL url = arg0;
        
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) 
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
        
        com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader
                .getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
        return extension.getRegistry(arg0);
    }

}

这里会调用 getRegistry 方法 且 url 参数是 multicast://…… ,适配类中 extName=“multicast”,所以会执行 RegistryFactory 适配类中的

ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(“multicast")

返回得到 MulticastRegistryFactory 实例类型,即回到 Debug-5 处,registryFactory就是 MulticastRegistryFactory 实例,调用的是 MulticastRegistryFactory 的 getRegistry 方法,由MulticastRegistryFactory源码知,MulticastRegistryFactory继承了 AbstractRegistryFactory 类且 MulticastRegistryFactory 中无 getRegistry 方法,所以会走向 AbstractRegistryFactory 的 getRegistry 方法,如下:
Debug-6:
在这里插入图片描述

createRegistry 本身是个抽象方法,具体看是使用的注册中心,如 zk注册中心就会调用 ZookeeperRegistryFactory 的 createRegistry 方法,redis 注册中心就会调用 RedisRegistryFactory 的 createRegistry 方法。这里调用的是MulticastRegistryFactory 的 createRegistry 方法。下面先讲解一下调用 MulticastRegistryFactory 的 createRegistry 方法过程,再解析一下调用 ZookeeperRegistryFactory 的 createRegistry 方法过程。

2.1.1 MulticastRegistryFactory的createRegistry方法解析过程

其中 url 变量值是:

multicast://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=51117&qos.port=33333×tamp=1542036492077

进入 MulticastRegistry 的对象创建过程,如下:
Debug-7:
在这里插入图片描述
Debug-8:
在这里插入图片描述
Debug-9:
在这里插入图片描述
由Debug-7知,MulticastRegistry构造时会首先调用父类构造函数,由于MulticastRegistry继承于FailbackRegistry,FailbackRegistry继承于AbstractRegistry,所以会先构造AbstractRegistry,构造过程如Debug-9,流程如下
1. 启动文件保存定时器

  • 首先检查url中的“save.file”参数,若没有设置,默认false。再从url中获取文件名称filename,若没有设置,默认如“dubbo-registry-127.0.0.1.cache”
    Dubbo的Consumer会将自己需要的Provider列表在本地保存一份,也包括自己暴露的服务信息(即自己也作为 Provider),为了防止注册中心挂掉获取不到注册信息。
  • 然后,再创建名为filename的文件,创建失败则抛出异常(这里的filename:/Users/zhuqiuhui/.dubbo/dubbo-registry-demo-consumer-224.5.6.7:1234.cache)
    2. loadProperties()把上面创建的缓存文件读进内存中
    在这里插入图片描述
    3. 先获取backup url,然后通知订阅
  • 获取backup URL。首先加入url自己,再从url参数中获取key是“backup”的url。
    在这里插入图片描述
  • 获取该注册中心的所有的订阅者,并通知到每个订阅者下的所有监听者(listener)。由于此时无该url的订阅者,所以直接跳过了该函数。

然后回到 Debug-8,回到FailbackRegistry的构造过程,可以看到构造过程中开启了一个线程来启动失败重试定时器,retryPeriod默认值5s。重试线程主要重试以下:

  • 注册失败的尝试注册
  • 取消注册失败的尝试取消注册
  • 订阅失败的尝试订阅
  • 取消订阅失败的尝试取消订阅
    在这里插入图片描述
    回到 Debug-7继续MulticastRegistry的构造过程,如下:
    在这里插入图片描述
    这样就回到了Debug-5完成注册中心的获取,其中registry变量值如下:
    在这里插入图片描述

2.2 消费端注册服务到注册中心

获取到注册中心后,接下来要对消费端进行服务注册,如下接着 Debug-5 往下走,
在这里插入图片描述

Debug-10:
在这里插入图片描述
Debug-10中各个变量值如下:
在这里插入图片描述
subscribeUrl详细值是:

consumer://172.17.13.52/org.apache.dubbo.demo.DemoService?application=demo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=11148&qos.port=33333&side=consumer×tamp=1542176660330

Debug-10 会调用 MulticastRegistry 的 register 方法(MulticastRegistry继承于FailbackRegistry,FailbackRegistry继承于AbstractRegistry),如下:
Debug-11:
在这里插入图片描述
Debug-12: FailbackRegistry中的 register方法
在这里插入图片描述
Debug-13: AbstractRegistry中的 register方法
在这里插入图片描述
url会被加到AbstractRegistry属性 registered Set集合中。
接着Debug-12,走到 doRegister 函数,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其中 msg:

register consumer://172.17.13.52/org.apache.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=2554&qos.port=33333&side=consumer×tamp=1542089038042

回到 Debug-11,即
在这里插入图片描述
在这里插入图片描述
回到Debug-10,即完成了把 url 注册到 MulticastRegistry 上。

2.3 创建RegistryDirectory,并订阅服务提供者

Debug-14:
在这里插入图片描述
进入RegistryDirectory中的subscribe方法,首先设置RegistryDirectory的consumerUrl,然后调用注册中心(MulticastRegistry实例)进行订阅。
在这里插入图片描述
进入MulticastRegistry实例的subscribe方法,如下:
Debug-15:
在这里插入图片描述
进入FailbackRegistry的subscribe方法,如下:
Debug-16:
在这里插入图片描述
进入AbstractRegistry的subscribe方法,把订阅关系放到 subscribed map <url,订阅该url的Listener>中,如下:
在这里插入图片描述
接着回到Debug-16,如下:
在这里插入图片描述
由于doSubscribe方法在FailbackRegistry中是抽象方法,需要调用其具体实现者,这里会调用到MulticastRegistry中doSubscribe方法的实现,订阅时需要广播订阅消息。如下:
在这里插入图片描述
这样Debug-16走完,回到Debug-15,
在这里插入图片描述
Debug-17:
在这里插入图片描述
Debug-17中,首先获取服务端的URL列表放到urls中,这里涉及到AbstractRegistry中的两个变量:

private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>();

subscribed存放<消费端url,订阅该url的Listener>
notified存放<消费端url,map<服务端名,服务端url列表>>
在这里插入图片描述

接着Debug-17走,如下:
在这里插入图片描述
会进入到FailbackRegistry的notify方法中,
在这里插入图片描述
在这里插入图片描述
会进入AbstractRegistry的notify方法中,
在这里插入图片描述

这里的listener实例是RegistryDirectory实例,调用RegistryDirectory实例的notify方法,表示对providers进行通知,如下:
在这里插入图片描述
上面根据url中的“category”不同做不同的处理(如providers,configurators,routers等等)。
再看 refreshInvoker 方法,该方法先对服务端url列表进行包装成新的Invoker列表,如下:
Debug-18:
在这里插入图片描述
对服务端url列表进行包装成新的Invoker列表,转换详情如下:
在这里插入图片描述
在这里插入图片描述
Debug-18中完成对服务端 url 列表转换后,再接着将invokers列表转成与方法的映射关系。至此,Debug-18完成,一直走到Debug-14完成。

二、创建服务代理

接着走ReferenceConfig的createProxy方法,核心代码如下:

proxyFactory.getProxy(invoker)

其中 proxyFactory 的适配器code是:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory {

    public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL          arg2) throws java.lang.Object {

        if (arg2 == null) 
            throw new IllegalArgumentException("url == null”);

        com.alibaba.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist”);

        if(extName == null) 
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy]));

        com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
    }


    public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
        if (arg0 == null) 
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        
        if (arg0.getUrl() == null) 
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();

        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) 
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy]));

        com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0);
    }
}

这里会调用:

ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");

上面会返回StubProxyFactoryWrapper实例,且StubProxyFactoryWrapper实例中拥有JavassistProxyFactory实例,所以会直接调用StubProxyFactoryWrapper的getProxy方法。
Debug-1:
在这里插入图片描述
由于StubProxyFactoryWrapper实例中拥有JavassistProxyFactory实例,所以Debug-1首先会调用 JavassistProxyFactory 的getProxy方法,但 JavassistProxyFactory 无 getProxy(Invoker invoker) 方法,所以会调用父类的getProxy方法,即会调用 AbstractProxyFactory 的 getProxy 方法,即:
在这里插入图片描述
在这里插入图片描述
上面的 getProxy 重载方法的实现在 JavassistProxyFactory 类中,所以会调用 JavassistProxyFactory 的getProxy方法实现,即返回了代理,如下:
在这里插入图片描述
这样就完成了服务代理的创建。

猜你喜欢

转载自blog.csdn.net/zhuqiuhui/article/details/84075521