dubbo源码分析-服务引用


服务引用发生在spring容器启动的时候,在容器启动时,spring会扫描dubbo自定义的相关xml schema,并通过自定义的DubboNamespaceHandler去处理,在DubboNamespaceHandler中注册了dubbo相关的所有标签,统一由DubboBeanDefinitionParser去处理解析。
服务引用标签对应的解析目标类是ReferenceBean,ReferenceBean维护了整个服务的生命周期,是服务引用的关键。
在spring容器进行初始化时,会将单例的bean放入容器中维护起来,也就是说ReferenceBean会在spring容器启动过程中初始化。 在这里插入图片描述可以理解为饿汉式单例模式,但它严格意义上来讲,并不是单例模式。
并且ReferenceBean还实现了一系列spring相关的接口,参与到spring容器启动的生命周期,比如FactoryBean、ApplicationContextAware、InitializingBean以及DisposableBean;FactoryBean会告诉容器具体注入的bean的类型,ApplicationContextAware的作用是将spring的应用上下文注入到SpringExtensionFactory,InitializingBean主要做的是在属性赋值之后,初始化之前,对属性做一些设置和校验,并且最终调用getObject(),将服务代理类注入容器。

引用目的

方便客户端透明的调用远程服务。

具体流程分析

  • 官方消费服务详细过程图:在这里插入图片描述
    从图上可知,服务引用大概就是将远端服务转化为Invoker,然后根据Invoker生成代理的过程。
  • ReferenceBean#getObject()–>ReferenceConfig#get()在这里插入图片描述
    *ReferenceConfig#get()–>ReferenceConfig#init()在这里插入图片描述
    防止重复引用,具体的引用逻辑下图中的代码完成在这里插入图片描述
  • ReferenceConfig#init()–》ReferenceConfig#createProxy()在这里插入图片描述
  1. 首先判断是否同一jvm引用服务
  2. 如果不是同一jvm引用,则判断是否是对点对直连地址
  3. 如果不是点对点直连,则加载注册中心连接,通过注册中心配置拼装服务URL
  4. 如果获取的远程服务url地址有多个(服务提供者集群),那么将会通过invoker集合和URL构造一个Directory,然后加入集群,返回一个AbstractClusterInvoker的实例。
    在这里插入图片描述
    集群相关需要关注的几个spi如下图:在这里插入图片描述
    在这里插入图片描述
    Cluster和Invoker的链接处如下图:在这里插入图片描述
    在这里插入图片描述
    也就是说集群情况下,返回的invoker是一个AbstractClusterInvoker的实例。
  5. 如果远程服务提供者只有一个,那么直接引用,refprotocol.refer()
    综上所述,我们现阶段只需要关注refprotocol.refer()的逻辑就可。
  • ReferenceConfig#createProxy()–》Protocol$Adpative.refer()在这里插入图片描述
    据上可知,最终调用的Protocol是RegistryProcotol
  • Protocol$Adpative.refer()–》ProtocolFilterWrapper.refer()
    因为协议是registry,所以不做处理
  • ProtocolFilterWrapper.refer()–》ProtocolListenerWrapper.refer()
    因为协议是registry,所以不做处理
  • ProtocolListenerWrapper.refer()–》RegistryProtocol.refer()
    在这里插入图片描述
  • RegistryProtocol.refer()–》registryFactory.getRegistry(url)
  • registryFactory.getRegistry(url)–》RegistryFactory$Adpative.getRegistry()
    在这里插入图片描述
    注册中心为zookeeper
  • RegistryFactory$Adpative.getRegistry()–》AbstractRegistryFactory.getRegistry()
    在这里插入图片描述
    获取注册中心AbstractRegistryFactory提供了统一处理的方案
    (1) 从REGISTRIES缓存(Map<String, Registry>)中获取
    (2)如果缓存中不存在,则调用createRegistry(url)创建
    调用createRegistry(url)创建过程上篇已经分析过,这里不在赘述。
    (3)将注册中心实例放入缓存,并返回
  • AbstractRegistryFactory.getRegistry()–》StringUtils.parseQueryString()
    在这里插入图片描述
    获取请求信息
  • StringUtils.parseQueryString()–》RegistryProtocol.doRefer()
    在这里插入图片描述
    (1)构造一个RegistryDirectory
    (2)构造订阅服务链接
    consumer://30.26.211.140/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=174404&side=consumer&timestamp=1542190681032
    
    (3)向zookeeper注册消费端地址
    consumer://30.26.211.140/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=174404&side=consumer&timestamp=1542190681032
    
    (4)向zookeeper订阅服务在zookeeper上的节点地址,在订阅节点发生变化时触发FailbackRegistry#notify,从而实现本地缓存文件的更新以及通知到RegistryProtocol.OverrideListener#notify(),重新生成invoker可执行对象
     ## RegistryDirectory 增加router的Directory
     public void subscribe(URL url) {
        setConsumerUrl(url);
        registry.subscribe(url, this);
    }
    

(5)生成Invoker
在这里插入图片描述
我们可以看到dubbo默认的容错策略是失败转移(Failover),并且外层会有一个MockClusterWrapper的包装类,用于本地伪装,做服务降级。

到此为止远端服务装化为Invoker的过程就结束了。

  • 创建代理的流程在这里插入图片描述
  • ReferenceConfig.proxyFactory.getProxy()–>ProxyFactory$Adpative.getProxy()
    在这里插入图片描述
    由上可知,默认使用javassist来实现代理,JavassistProxyFactory类图如下:
    在这里插入图片描述
  • ProxyFactory$Adpative.getProxy()–>StubProxyFactoryWrapper.getProxy()
  • StubProxyFactoryWrapper.getProxy()–》AbstractProxyFactory.getProxy()
  • AbstractProxyFactory.getProxy()–》JavassistProxyFactory.getProxy()
  • JavassistProxyFactory.getProxy()–》Proxy.getProxy()
    (1)PendingGenerationMarker:保证每个代理类当前只允许有一个线程生成。
    (2)代理类的存放采用new WeakReference(proxy)的方式,方便和加快垃圾回收,避免长时间不用的代理对象长驻内存 Proxy.getProxy()–》Proxy.newInstance()
    (3)ClassGenerator:dubbo自已对javassist的封装
    到此创建服务代理就完结了,完成了从Invoker到ref的转换。
  • 然后将创建好的代理类维护到spring容器中,让spring管理起来。在这里插入图片描述
    具体的位置在在这里插入图片描述
    也就是说FactoryBean创建的对象都在factoryBeanObjectCache中保存。这样就可以方便的从容器中获取代理服务,执行相应的操作。
    至此,整个引用流程就结束了。

引用总结

服务引用简单来说,就是文章开篇官方图描绘的那样,需要注意的几点是:引用中获取的Invoker是经过包装的(集群容错等伪装成一个Invoker);引用过程会处理是否本地暴露以及点对点直连;注册的zookeeper节点发生变化时,会刷新本地缓存;默认是由javassist创建代理服务,同时也提供jdk创建代理服务的方式。

简单时序图

在这里插入图片描述
时序图来源简书地址

官方时序图

在这里插入图片描述
下集预告:服务消费

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值