消费者在启动之后,会通过ReferenceConfig#get()
来生成远程调用代理类。在get
方法中,会启动一系列调用函数,我们来一个个解析。
配置同样包含2种:
new ReferenceConfig
在此阶段,会初始化org.apache.dubbo.config.AbstractConfig
& org.apache.dubbo.config.ReferenceConfig
的静态变量以及静态代码块。
ReferenceConfig#get
ReferenceConfig#init
DubboBootstrap
启动dubbo。
URL.buildKey(interfaceName, group, version)
这段用来生成唯一服务的key,所以我们之前说dubbo的唯一标识是通过全路径
和group以及version来决定的。
org.apache.dubbo.config.utils.ConfigValidationUtils#checkMock
来检查我们mock是否设置正确。dubbo
的注册地址,默认为当前主机IPReferenceConfig#createProxy
创建调用代理开始
ReferenceConfig#shouldJvmRefer
首先判断是否为Injvm
调用injvm
,判断是否为peer to peer
端对端设置,如果为p2p,那么就直连urlURL
REF_PROTOCOL.refer(interfaceClass, urls.get(0));
来将URL
转为Invoker
对象,因为private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
是扩展是Adaptive
,因此在这里会执行Protocol$Adaptive#refer
方法,又由于protocol
参数值为registry
,因此会只是RegistryProtocol#refer
,又由于被Wrapper
类装配,因此会先执行三个Wrapper类,最后才能执行到RegistryProtocol#refer -> RegistryProtocol#doRefer
,在doRefer
方法中会订阅服务提供者地址,最后返回Invoker
对象。!那么究竟是如何生成的Invoker
对象呢?我们来看下具体代码:
上述代码中,步骤1根据URL生成了一个RegistryDirectory
(关于Directory接口的作用,可以自行查询一些,直白一些就是将一堆Invoker对象封装成一个List,只有2种实现RegistryDirectory & StaticDirectory
,从命名可看出一个是动态可变,一个不可变),代码2 封装了订阅所要使用的参数信息,代码3则是封装绑定路由规则链,代码4订阅。代码5调用 Cluster$Adaptive#join
方法生成Invoker
对象。
在代码2种从zk获取服务提供者信息:
一旦zk返回服务提供者列表之后,就会调用RegistryDirectory#notify
,如下:
在org.apache.dubbo.common.utils.UrlUtils#isMatch
中对provider和consumer的api进行匹配校验。继续跟踪:RegistryDirectory#notify -> RegistryDirectory#refreshOverrideAndInvoker -> RegistryDirectory#refreshInvoker -> RegistryDirectory#toInvokers
在toInvokers
正式将URL转换为Invoker
,通过invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
在这里protocol#refer
同样执行顺序如:
(dubbo 2.7.5) protocol#refer -> protocol$Adaptive#refer -> QosProtocolWrapper#refer -> ProtocolListenerWrapper#refer -> ProtocolFilterWrapper#refer ->AbstractProtocol#refer->DubboProtocol#protocolBindingRefer
,调用代码如下:
关注getClients
,其中执行了DubboProtocol#getSharedClient -> DubboProtocol#initClient
创建netty client进行连接。
因为这里使用的是明确的DubboInvoker
,在回调的时候,Wrapper会对该Invoker进行包装,执行效果如下:
因此,会执行到ProtocolFilterWrapper#buildInvokerChain
,该函数会对服务进行调用链跟踪:
所有的负载均衡、容错策略等都是在这里绑定的。
7.如果有多个注册中心,将会循环执行步骤6,将URL转换为Invoker
对象,然后添加到一个List,分别进行注册之后,然后判断最后一个注册中心url
是否有效,针对多订阅的场景,URL中添加cluster
参数,默认使用org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster
策略,使用org.apache.dubbo.rpc.cluster.Cluster#join
将多个Invoker
对象封装一个虚拟的Invoker
中,否则如果最后一个注册中心是无效的,直接封装Invoker
对象。
8.创建服务代理ProxyFactory#getProxy(org.apache.dubbo.rpc.Invoker<T>)
,因为ProxyFactory
是一个适配类。那么同样这里会调用ProxyFactory$Adaptive#getProxy
,这里最终就是返回一个代理服务的Invoker对象。
至此,我们的消费端的绑定远程zk的服务就已经结束了。 那么,我们在调用服务器方法的时候服务器端和客户端都是如何处理的呢?下节我们将继续分析。