Dubbo——消费端启动过程分析

消费端启动过程分析

1 触发服务引用过程

        消费端的启动过程中,对服务引用由ReferenceBean类开始处理逻辑,该类继承了标签对应的配置类ReferenceConfig,还实现了一系列Spring接口用于参与Spring容器的启动以及bean的创建过程中去。ServiceBean类实现了InitializingBean接口的afterPropertiesSet方法。

        对于注解方式引用服务,首先在在Spring回调AnnotationBean类的postProcessBeforeInitialization方法时,触发调用ReferenceBean. afterPropertiesSet ()方法,若引用服务的注解属性init设置为true,则在Spring的初始化过程中进行初始化引用服务,否则等到应用注入或引用实例时再初始化,在Spring中初始化时通过ReferenceBean.getObject() 间接调用ReferenceConfig.get() 方法完成引用服务的初始化。

        对于XML方式引用服务,在spring的容器ApplicationConetext的启动过程refesh过程中最后第二步会预先初始化单例bean,在bean的初始化过程中会设置beanName,设置容器的appliactionContext并回调afterPropertiesSet方法,若此引用服务设置为立即初始化,则通过ReferenceBean.getObject() 间接调用ReferenceConfig.get() 方法完成引用服务的初始化。

        上述设置服务引用延迟的方法是配置标签的init属性。


2 检查点对点直连配置

配置点对点直连的方式有三种,分别如下:

1、在中配置url指向提供者,将绕过注册中心,多个地址用分号隔开;

2、在JVM启动参数中加入-D参数映射服务地址,key为服务名,value为服务提供者url;

例如:java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890

3、如果服务比较多,也可以用文件映射,用-Ddubbo.resolve.file指定映射文件路径,例如:java -Ddubbo.resolve.file=xxx.properties

然后在映射文件xxx.properties中加入:(key为服务名,value为服务提供者url),

例如:com.alibaba.xxx.XxxService=dubbo://localhost:20890

       在ReferenceConfig.init()方法中,首先通过第2种方式获取服务地址,若没有配置再通过第3种方式获取服务地址,在第3种方式中优先获取${user.home}/dubbo-resolve.properties文件,若没有在使用-Ddubbo.resolve.file指定的映射文件,最后在前两种方法均没有获取到服务地址的情况下,取第1种方式中配置的服务地址。


3 检查是否引用本地服务

配置是否引用本地服务的方式有如下5种,按照以下5种方式顺序检查是否引用本地服务。

1、配置,injvm为true表示引用本地服务;为false表示引起远程服务;

2、配置,name为injvm表示引用本地服务;

3、配置,scope为remote表示引用远程服务;为local表示引用本地服务;

4、配置<,generic为false表示引用本地服务,为true表示引用远程服务,默认为false。

5、检查本地是否有服务暴露,有则引用本地服务。


4 引用服务

4.1 本地引用服务

  代理工厂类Protocol$Adpative根据URL中的protocol选择InjvmProtocol来处理本地引用,所有Protocol类均被ProtocolFilterWrapperProtocolListenerWrapper封装。

       首先调用InjvmProtocol.refer()方法,把服务转换成Invoker,即初始化InjvmInvoker对象;

       然后在ProtocolListenerWrapper是对Invoker构建了监听器链并封装成ListenerInvokerWrapper对象返回。大致逻辑是:1)获取扩展点为InvokerListener.class的自动激活的实例列表(即有@Active注解的类的实例)以及消费端设置的引用监听器类实例(由< dubbo:reference >标签的listener属性设置,在URL中存为参数“invoker.listener”);2)遍历第1步的监听器实例列表,执行监听器实例的refered方法。Dubbo没有实现监听器,但提供了业务扩展的接口,业务可以实现InvokerListener接口或InvokerListenerAdapter抽象类来,并在xml标签listener属性中配置监听器的类名即可。

      再次在ProtocolFilterWrapper类中对上面步骤返回的Invoker对象添加过滤器链,过滤器链包括两部分,1)选择Filter.class扩展点中存在@Active注解并且group值等于“consumer”的过滤器实例,2URL参数“reference.filter”的类名(在< dubbo:reference >标签的filter属性设置),调用getExtension方法获取实例对象;目前有这些消费端过滤器MonitorFilterValidationFilterCacheFilterFutureFilterGenericImplFilterActiveLimitFilterConsumerContextFilterDeprecatedFilter

        最后将封装后的Invoker对象返回。

4.2 远程引用服务

  若未配置直连URL,则通过3.2节的方式获取注册地址。遍历所有的注册地址,进行远程引用服务逻辑处理。调用代理工厂Protocol$Adpative根据注册地址URL中的协议选择具体的Protocol处理类,此处选择了RegistryProtocol. refer ()方法处理服务引用的事情。具体逻辑如下:

      若未配置直连URL,则通过3.2节的方式获取注册地址。遍历所有的注册地址,进行远程引用服务逻辑处理。调用代理工厂Protocol$Adpative根据注册地址URL中的协议选择具体的Protocol处理类,此处选择了RegistryProtocol. refer ()方法处理服务引用的事情。具体逻辑如下:

       1 、根据注册地址 URL 获取 Registry 实例,在动态生成的代理工厂 RegistryFactory$Adpative 中根据注册协议选择具体的 RegistryFactory ,然后调用来 getRegistry 方法创建相应的 Registry 实例,具体的处理逻辑在 《各注册协议的处理逻辑》 中讲解。目前支持的注册协议有 dubbo multicast zookeeper redis ,在 RegistryFactory 文件中有配置(如下),以 zookeeper 协议为例,首先创建 ZookeeperRegistryFactory 工厂类实例,然后调用 ZookeeperRegistryFactory getRegistry 方法返回 ZookeeperRegistry 实例,在初始化该 ZookeeperRegistry 实例的过程中完成了与注册中心的建链,并对注册中心的状态设置了监听器

Dubbo——消费端启动过程分析_第1张图片

2、若引用的服务classRegistryService.class(即是注册中心RegistryService服务),则直接返回该服务的Invoker代理,具体方法由动态代理工厂ProxyFactory$Adpative根据proxy参数选择具体的代理工厂,默认选择JavassistProxyFactory,调用JavassistProxyFactory.getInvoker方法创建AbstractProxyInvoker对象的代理类,然后跳出此次远程引用服务逻辑。

3、选择集群策略:若标签group属性值为“*”或有多个时,则创建MergeableCluster类作为集群策略处理类。若没有则使用默认的FailoverCluster类作为集群策略处理类。

 4、初始化RegistryDirectory对象,该对象的类实现了NotifyListener的一个监听器,可以随着注册中心的消息推送而动态变化服务的Invoker,时刻监听着服务提供者的变化,封装了所有服务真正的引用逻辑、覆盖配置、路由规则等逻辑,初始化时向注册中心发起订阅请求,并主动调用notify方法,完成服务提供者URL的获取以及建链操作,具体逻辑如下:

4.1)组装服务消费端的URL地址,然后将该消费端URL地址向注册中心进行注册。以zookeeper协议为例,此过程是调用ZookeeperClientcreate方法在zookeeper中创建一个节点树,将服务消费端的URL作为数据存入consumers节点的叶子节点中,consumers节点的上一级是服务的类名。具体的处理逻辑在《各注册协议的处理逻辑》中讲解。

4.2)向注册中心的服务节点下面的providersconfiguratorsrouters节点订阅监听器(RegistryDirectory对象)。同时执行RegistryDirectory.notify()方式。其中,RegistryDirectory.notify()处理逻辑:

   1)对routers节点下面的数据进行监听,设置AbstractDirectory.routersList对象,在获取Invoker列表的过程中进行路由选择时使用。若通过Dubbo管理系统为服务设置路由参数,则动态配置的参数放在routers节点目录下,并触发监听器RegistryDirectory对象对routers变量进行更新。

   2)对configurators节点下面的数据进行监听,将此节点下面的数据设置到RegistryDirectory.overrideDirectoryUrl参数上。

   3)对providers节点下面的数据进行监听,该节点下面的数据是服务提供者的URL列表,根据这些URL创建消费端的Invoker对象。具体过程如下:

   3.1)若此列表里面只有一条empty协议的URL地址,则销毁本地该服务对应的所有的Invoker

   (3.2)根据服务提供者的URL使用的协议选择具体的Protocol类的refer方法来处理,该方法创建了具体的Invoker对象(Dubbo协议创建DubboInvoker对象),具体Protocol类的refer方法处理逻辑见《各类协议引用服务的逻辑》

   (3.3)由RegistryDirectory$InvokerDelegete类对Invoker对象进行封装,并存入URL对应Invoker的映射关系表RegistryDirectory. urlInvokerMap: Map>中,其中key为服务提供者的URL

    (3.4)将urlInvokerMap列表转换成与方法的映射关系methodInvokerMap列表,表示每个方法对应的Invoker列表,在转换过程中根据注册中心中每个方法的路由过滤规则过滤掉部分Invoker对象。

   (3.5)对配置了group为多组的服务引用,将相同的组合并到同一列表中。

4.3默认使用FailoverCluster集群策略类,调用该FailoverCluster类的join(Directory directory)方法将RegistryDirectory对象封装成FailoverClusterInvoker对象并返回。由于Cluster类名文件中MockClusterWrapper类存在以该Cluster对象为参数的构造函数,故调用MockClusterWrapper.join方法将FailoverClusterInvoker对象再次封装,封装成MockClusterInvoker对象并返回

4.3 创建服务类的本地代理

     在完成远程服务的引用之后得到了Invoker代理对象,下面继续对该代理对象创建本地代理提供给应用层调用,大致逻辑如下:   

       用ExtensionLoader动态生成的代理工厂ProxyFactory$Adpative根据URL中的参数proxy选择具体的代理工厂类来创建服务类的本地代理,默认选择JavassistProxyFactory代理工厂,调用其getProxy方法来创建代理。代码如下:


1)从URL的参数interfaces中获取服务类的类名;

2)用在引用服务过程中创建的Invoker对象初始化InvokerInvocationHandler类,该类实现了java.lang.reflect.InvocationHandler类。当消费端远程调用服务时,通过动态代理在此类的invoke方法中调用上述Invoker对象的invoke方法。

至此,完成了消费端远程服务本地代理的创建工作,消费端的应用层可以通过调用此动态代理发起远程服务的调用工作,即调用InvokerInvocationHander.invoke方法。

Dubbo——消费端启动过程分析_第2张图片

引用服务的活动图

Dubbo——消费端启动过程分析_第3张图片


你可能感兴趣的:(dubbo)