消费端的启动过程中,对服务引用由ReferenceBean类开始处理逻辑,该类继承了
对于注解方式引用服务,首先在在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() 方法完成引用服务的初始化。
上述设置服务引用延迟的方法是配置
配置点对点直连的方式有三种,分别如下:
1、在
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种方式中配置的服务地址。
配置是否引用本地服务的方式有如下5种,按照以下5种方式顺序检查是否引用本地服务。
1、配置
2、配置
3、配置
4、配置<
5、检查本地是否有服务暴露,有则引用本地服务。
代理工厂类Protocol$Adpative根据URL中的protocol选择InjvmProtocol来处理本地引用,所有Protocol类均被ProtocolFilterWrapper、ProtocolListenerWrapper封装。
首先调用InjvmProtocol.refer()方法,把服务转换成Invoker,即初始化InjvmInvoker对象;
然后在ProtocolListenerWrapper是对Invoker构建了监听器链并封装成ListenerInvokerWrapper对象返回。大致逻辑是:1)获取扩展点为InvokerListener.class的自动激活的实例列表(即有@Active注解的类的实例)以及消费端设置的引用监听器类实例(由< dubbo:reference >标签的listener属性设置,在URL中存为参数“invoker.listener”);2)遍历第1步的监听器实例列表,执行监听器实例的refered方法。Dubbo没有实现监听器,但提供了业务扩展的接口,业务可以实现InvokerListener接口或InvokerListenerAdapter抽象类来,并在xml的
再次在ProtocolFilterWrapper类中对上面步骤返回的Invoker对象添加过滤器链,过滤器链包括两部分,1)选择Filter.class扩展点中存在@Active注解并且group值等于“consumer”的过滤器实例,2)URL参数“reference.filter”的类名(在< dubbo:reference >标签的filter属性设置),调用getExtension方法获取实例对象;目前有这些消费端过滤器MonitorFilter、ValidationFilter、CacheFilter、FutureFilter、GenericImplFilter、ActiveLimitFilter、ConsumerContextFilter、DeprecatedFilter。
最后将封装后的Invoker对象返回。
若未配置直连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 实例的过程中完成了与注册中心的建链,并对注册中心的状态设置了监听器。
2、若引用的服务class为RegistryService.class(即是注册中心RegistryService服务),则直接返回该服务的Invoker代理,具体方法由动态代理工厂ProxyFactory$Adpative根据proxy参数选择具体的代理工厂,默认选择JavassistProxyFactory,调用JavassistProxyFactory.getInvoker方法创建AbstractProxyInvoker对象的代理类,然后跳出此次远程引用服务逻辑。
3、选择集群策略:若
4、初始化RegistryDirectory对象,该对象的类实现了NotifyListener的一个监听器,可以随着注册中心的消息推送而动态变化服务的Invoker,时刻监听着服务提供者的变化,封装了所有服务真正的引用逻辑、覆盖配置、路由规则等逻辑,初始化时向注册中心发起订阅请求,并主动调用notify方法,完成服务提供者URL的获取以及建链操作,具体逻辑如下:
4.1)组装服务消费端的URL地址,然后将该消费端URL地址向注册中心进行注册。以zookeeper协议为例,此过程是调用ZookeeperClient的create方法在zookeeper中创建一个节点树,将服务消费端的URL作为数据存入consumers节点的叶子节点中,consumers节点的上一级是服务的类名。具体的处理逻辑在《各注册协议的处理逻辑》中讲解。
4.2)向注册中心的服务节点下面的providers、configurators、routers节点订阅监听器(RegistryDirectory对象)。同时执行RegistryDirectory.notify()方式。其中,RegistryDirectory.notify()处理逻辑:
1)对routers节点下面的数据进行监听,设置AbstractDirectory.routers:List
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
(3.4)将urlInvokerMap列表转换成与方法的映射关系methodInvokerMap列表,表示每个方法对应的Invoker列表,在转换过程中根据注册中心中每个方法的路由过滤规则过滤掉部分Invoker对象。
(3.5)对配置了group为多组的服务引用,将相同的组合并到同一列表中。
4.3)默认使用FailoverCluster集群策略类,调用该FailoverCluster类的join(Directory
在完成远程服务的引用之后得到了Invoker代理对象,下面继续对该代理对象创建本地代理提供给应用层调用,大致逻辑如下:
用ExtensionLoader动态生成的代理工厂ProxyFactory$Adpative根据URL中的参数proxy选择具体的代理工厂类来创建服务类的本地代理,默认选择JavassistProxyFactory代理工厂,调用其getProxy方法来创建代理。代码如下: