dubbo源码浅析(四)-服务消费者初始化

时间:2022-01-23 22:00:24

在分析标签解析的时候知道框架会把dubbo:reference解析成一个ReferenceBean,它是一个FactoryBean,消费者的初始化在它的init方法中执行,这个方法在两种情况下会被调用:
1. 消费者设置了立即初始化(init属性设置成true),那么bean加载时会立刻调用消费者初始化。
2. 消费者bean被使用者调用时,调用getObject->get->init。

消费者初始化时主要做的事情就是引用对应的远程服务,执行了以下步骤:
1. 监听注册中心
2. 连接服务提供端
3. 创建消费端服务代理

监听注册中心

消费者在初始化时也会生成一个registryUrl,消费端的信息:side=consumer、dubbo版本、时间戳、应用名等拼成query串放在registryUrl的refer参数中,消费者初始化时引用远程服务也由Protocol组件来处理,加载自适应的Protocol实现:

com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map<String, String>)

dubbo源码浅析(四)-服务消费者初始化
传入的url参数是注册中心URL,同样地也会加载到ProtocolListenerWrapper和ProtocolFilterWrapper这两个Wrapper扩展点,然后依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol实例的refer方法:
dubbo源码浅析(四)-服务消费者初始化
dubbo源码浅析(四)-服务消费者初始化
registry协议的url两个wrapper不会进行任何处理直接进入RegistryProtocol.refer。在这个方法中先把消费者注册到注册中心,zookeeper注册中心由ZookeeperRegistry来处理,在zookeeper服务器上生成一个节点,节点路径是/dubbo/ interfaceName/customs/ {customUrl},存储了服务消费方ip、group、接口名称、版本、应用名称等,customUrl是consumer://10.240.176.159/com.netease.haitao.payment.remote.api.coupon.SendCouponRemoteApi?application=…这种形式的,在注册之前生成,把本机ip、接口名称等添加到URL中:

com.alibaba.dubbo.registry.integration.RegistryProtocol.doRefer(Cluster, Registry, Class, URL)
dubbo源码浅析(四)-服务消费者初始化
消费端本地会缓存远程服务提供者(每个提供者对应一个Invoker对象)、注册中心配置、路由配置信息。监听注册中心路径是/dubbo/ interfaceClass/providers/dubbo/ {interfaceClass}/configurators, /dubbo/${interfaceClass}/routers的节点,当提供者、配置、路由信息发生变化之后注册中心会通知消费者刷新本地缓存。Dubbo框架通过在消费端缓存提供者的信息消除对注册中心的强依赖,即使注册中心挂了服务依然可用。

com.alibaba.dubbo.registry.integration.RegistryProtocol.doRefer(Cluster, Registry, Class, URL)
dubbo源码浅析(四)-服务消费者初始化
框架为每个接口消费者创建一个RegistryDirectory对象,缓存接口所有的提供端Invoker以及注册中心接口相关的接口配置configurators,服务提供者Invoker保存在RegistryDirectory的methodInvokerMap中,key是方法名称或者,因为大系统中同一个服务一般会由多台服务器提供,所以value是一个Invoker列表存储该服务接口的所有提供者,一般情况下,假如服务接口中有a、b两个接口,那么map中的内容是:{a=invokers,b=invokers,=invokers},其中invokers列表中的内容相同,服务被调用时会根据方法名称从这个map中查找提供者对应Invoker。当providers节点发生变化时通知消费端更新缓存的providers对应的Invoker列表。

com.alibaba.dubbo.registry.integration.RegistryDirectory.refreshInvoker(List)
dubbo源码浅析(四)-服务消费者初始化
接下来要给每个提供者创建一个对应的Invoker,refreshInvoker方法调用toInvokers,在toInvokers中加载自适应Protocol实现,并且调用它的refer方法,此时url参数是从注册中心获取到的携带提供者服务信息的providerUrl,根据扩展点加载规则,会依次调用ProtocolListenerWrapper->ProtocolFilterWrapper-> DubboProtocol的refer方法,在这两个Wrapper中添加对应的InvokerListener并且构建Invoker-Filter链,最后在DubboProtocol.refer中创建一个DubboInvoker对象,该Invoker对象持有服务Class、providerUrl、负责和提供端通信的ExchangeClient,Invoker对象保存在DubboProtocol的invokers集合中。

com.alibaba.dubbo.registry.integration.RegistryDirectory.toInvokers(List)
dubbo源码浅析(四)-服务消费者初始化

连接服务提供端

构建DubboInvoker时,会构建一个或多个ExchangeClient用来处理和提供端的连接,默认情况下一个url只会创建一个ExchangeClient负责和对应的提供端建立连接,如果应用方配了多个connections会创建多个。如果lazy属性没有设置成true(默认false),则此时ExchangeClient会马上和服务端建立连接,此时组件调用顺序:Exchanger.connect->Transporter.connect ->Client,最终会调用Netty框架和提供端建立连接,创建NettyClient对象。

com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
dubbo源码浅析(四)-服务消费者初始化
和NettyServer一样,NettyClient在也会注册IO事件处理链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应编解码、响应处理。

com.alibaba.dubbo.remoting.transport.netty.NettyClient.doOpen()
dubbo源码浅析(四)-服务消费者初始化

创建消费端服务代理

回到RegistryProtocol .doRefer方法的最后,由Cluster组件来创建一个Invoker并返回,此处的Cluster也是自适应的实现,示例配置的容错模式是failfast,返回扩展链MockClusterWrapper->FailfastCluster(默认是failover),经过扩展链处理创建MockClusterInvoker->FailfastClusterInvoker对象链, FailfastClusterInvoker的directory属性引用上面创建的RegistryDirectory对象。
dubbo源码浅析(四)-服务消费者初始化
dubbo源码浅析(四)-服务消费者初始化
返回生成的Invoker实例之后,由ProxyFactory生成一个持有该Invoker实例的代理,代理回调时会激活该Invoker(MockClusterWrapper类型)调用它的invoke方法:
com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
dubbo源码浅析(四)-服务消费者初始化
dubbo源码浅析(四)-服务消费者初始化
dubbo源码浅析(四)-服务消费者初始化
该代理会被注册到spring IO容器中,之后业务中从容器中获取消费者bean时容器会返回这个代理。