Dubbo源码深度解析(六)

        上一篇博客《Dubbo源码深度解析(五)》主要讲:当服务消费方发起请求时,服务提供方是通过Netty服务接受请求并处理的,涉及到Netty相关使用及部分原理的讲解,以及最后又是如何将Invoker对象的执行结果返回给服务消费方的等。同时也讲了服务消费方的调用链路,这块还是比较复杂的。难点在于:要对Dubbo的SPI机制很熟悉,才知道具体是哪个接口实现类,并且还涉及到接口的包装类,以及对真正干活的Invoker的层层包装;如果对Dubbo理解和使用不够深入,可以借助debug,但如果没有真的理解Dubbo的SPI机制,是无法搞清楚为什么最终得到的Invoker对象会经过那么多层包装,而哪些包装的类又是怎么来的;还有就是Dubbo中很多地方,都是动态生成字节码对象,如果对字节码对象中接口的实现方法不熟悉的话,也很难继续看下去。

        这篇博客,将从服务消费方开始讲:被@DubboReference修饰的属性,是如何得到实现类并进行属性注入的,以及服务消费方最终是如何调用到服务提供方的。这里我就默认各位都认真看过了我前面写的五篇Dubbo源码解析博客。因此,我不会再像之前一样,讲得那么仔细了。

        这里可以考虑几个问题:

① 对被@DubboReference修饰的属性进行依赖注入,但服务消费方本地并没有定义接口实现类,因此,最终注入的对象,一定是代理对象;

② 既然是属性注入,多半是类似@Autowired注解,Dubbo一定会提供一个BeanPostProcessor,专门处理@DubboReference注解。这跟服务提供方处理@DubboService注解还不一样,因为它是创建Bean对象并注册到Spring容器中,因此需要借助于BeanFactoryPostProcessor或者ImportBeanDefinitionRegistrar、ImportSelector(需借助@Import注解)来处理,因为这两种接口的执行时机是在BeanPostProcessor之前,准确地讲是生成BeanDefinition阶段(如包扫描、处理配置类),因为Spring最终是根据BeanDefinition来生成Bean的。根据我前面讲的内容可以知道,服务提供方就是通过ServiceAnnotationBeanPostProcessor来处理的(BeanFactoryPostProcessor的实现类)。

③ 服务消费方是如何跟服务提供方进行通讯的?毫无疑问也是使用的Netty,这里肯定也涉及到注册中心,因为服务消费方肯定是从注册中心拉取对应服务提供方的相关信息,如IP/PORT等,同时还要考虑服务提供方配置的变更,因此,服务消费方肯定会定期拉取服务消费方的信息并更新到自己本地。如果服务提供方有多个节点,那是不是还要考虑负载均衡呢?因此负载均衡这块肯定是在服务消费方发起调用的时候实现的,参考之前服务提供方之前的FilterNode链,可能服务消费方也会有类似的处理,当然这都只是猜测,具体可以看源码,来验证自己的猜测。

        开始今天的内容。关于上文说的处理@DubboReference注解,其实在之前的博客中有提到,就是在@EnableDubboConfig注解中做了处理,代码如下:

ba7d581205f849f0a6105796a7ac749f.png

03e94e9cad6345ff8359cdf93637b803.png

        DubboBeanUtils#registerCommonBeans()方法中,就往Spring容器中注册了一个后置处理器,即ReferenceAnnotationBeanPostProcessor,看名字也能知道,肯定是它处理的@DubboReference注解,注解看看这个后置处理器,先看看它的类继承结构,结果如下:

abe44df3dc564063b178818ccdb88f8d.png

        可以知道,ReferenceAnnotationBeanPostProcessor实现了InstantiationAwareBeanPostProcessor接口,看看该接口,代码如下:

e444c0e5c799447a94088b1de000fd45.png

        如果对Spring源码熟悉的话,就知道AutowiredAnnotationBeanPostProcessor实现了该接口,并且实现了postProcessProperties()方法,在该方法中处理被@Autowired注解修饰的类的成员属性或者成员方法,并实现属性的依赖注入。ReferenceAnnotationBeanPostProcessor也类似,但它实现的是postProcessPropertyValues()方法,但是根据Spring源码可知,这也没问题(建议通过实现postProcessProperties()方法实现依赖注入,因为postProcessPropertyValues()是一个过期的方法,后续Spring版本会弃用),Spring核心代码如下:

b9c22bd038054b069635eebe9d78d831.png

        看看 ReferenceAnnotationBeanPostProcessor的无参构造,代码如下:

17b7ca7af58d43ada7057ed3ff3ab30d.png

5fc8f81c6a8945c986c4c107276ba1d4.png

        再看看 ReferenceAnnotationBeanPostProcessor#postProcessPropertyValues()方法,代码如下:

284c75da85f34a7bb020323343a26802.png

        看看AbstractAnnotationBeanPostProcessor#findInjectionMetadata()方法,代码如下:

67411bfd6b404d89bb5f406c9606e27e.png

748d2383f5634177b8a5cb57e7ddabb1.png

        以字段被@DubboReference注解修饰为例 ,看看AbstractAnnotationBeanPostProcessor#findFieldAnnotationMetadata()方法,代码如下:

39e11c7279da413e8295362f5aef6f2f.png

        再回到ReferenceAnnotationBeanPostProcessor#postProcessPropertyValues()方法,该方法中,调用的是AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata#inject()方法,代码如下:

631d926c16a9423cbf7eab5567f14db7.png

5e5a2bb1f8d541e6908d48f6f66b4af3.png

8f1a341d212d4f8e896a18930185b10b.png

cb6807e26cc542369aa8f6a576fce4f6.png

        再看看AbstractAnnotationBeanPostProcessor#getInjectedObject()方法,看看是如何获取带注入的对象的,代码如下:

6baeaddc3421490491c37251439b6494.png

        继续看ReferenceAnnotationBeanPostProcessor#doGetInjectedBean()方法,代码如下:

b90284b2a22d4bccbc2f21c3a29286b7.png

        看ReferenceAnnotationBeanPostProcessor#buildReferenceBeanIfAbsent()方法,代码如下:

92dc6475667e4c7fb1950bd9ff3dc185.png

        AnnotatedInterfaceConfigBeanBuilder#build()方法,代码如下:

879d9610cca346458b2359de32578872.png

240fad329da74448a014b9b930601060.png

        再看看AnnotatedInterfaceConfigBeanBuilder#configureBean()方法,代码如下:

ae40b14470d540b98af43850763b086f.png

        需要要提一下的是,此时RegistryConfig对象(注册中心)没有设置给ReferenceBean对象,因为代码中会判断@是否给@DubboReference注解设置了registry属性(即注册中心名),具体代码如下:

f7795d7e18c64f06a4b8c9b61a5e194d.png

cab9f908b6ac44da924886d3c85e06e8.png

        再看看ReferenceBeanBuilder#postConfigureBean()方法,代码如下:

37bd8281882d4558a517117003e79af3.png

                既然是初始化方法,当然需要看看afterPropertiesSet()方法,代码如下:

d3acbd66557d4e1780dd82b37911cc3a.png

        ReferenceAnnotationBeanPostProcessor#doGetInjectedBean()方法,再看ReferenceAnnotationBeanPostProcessor#registerReferenceBean()方法,代码如下:

8676ba4edbf9464a86be7b94dee5eb7f.png

        最后调用ReferenceBean.get()方法,代码如下:

7ab2185b60074448a77c9bcf533ead79.png

d1595445ffbf455c8363d7a1cee4c7ed.png

        看看ReferenceBean#init()方法,代码如下:

d406166ce36f47fda89cf86ea1401091.png

7518776334f54088afef12ccef42e3a6.png

        重点看ReferenceBean#createProxy()方法,代码如下:

65cf49acdfd34532b61e7332fa88842c.png

4cbb31365cf5401e83c1b9a511f78f9a.png

9720d7c1a82f40a7a2d4802ede8bcd9b.png

        再看看ConfigValidationUtils#loadRegistries()方法,代码如下:

4698ab095d8f4fe9a2e06fd19de355e3.png

2d98bac4ef93412cbc65cac51440cca0.png

        根据前面将服务提供方,也能知道这里的REF_PROTOCOL属性实际上是Protocol$Adaptive对象,并且由于url中的协议是"registry",因此最终得到调用的是RegistryProtocol#refer()方法,代码如下:

5d4ab772e14e4e4380fcae97334b925f.png        看看Cluster#getCluster()方法,代码如下:

cf497a422d4b4d6aad16462642ac57cc.png

        再看看org.apache.dubbo.rpc.cluster.Cluster文件的内容,结果如下:

c650fb25f1c24a9d981c4cb1783f9260.png

        因此最终得到的Cluster为MockClusterWrapper对象,而MockClusterWrapper对象的cluster属性为FailoverCluster对象,断点验证,结果如下:

0e8a04bc3b074c3eb3c8336a34104612.png

        重点看RegistryProtocol#doRefer()方法,代码如下:

b126c7f841594c8fa5a3cdd4681d65b8.png

        调用RegistryProtocol#getMigrationInvoker()方法,实际上是调用RegistryProtocol子类的getMigrationInvoker()方法,即:

2a174c7e03d24568b0536a4081a24883.png

7a18b10cc06c4adf884f03a35e1778aa.png

        再看RegistryProtocol#interceptInvoker()方法,代码如下:

fdfcd26c512a46fbad6ef20591c2c626.png

c29e9937da804d2f9fd6add7e4710e66.png

        看看MigrationRuleListener类,发现rawRule是在其无参构造中赋值的,并且结果为"INIT":

a1bee18615e84c908955cc20ff2c68aa.png

        再看MigrationRuleHandler#doMigrate()方法,代码如下:

404dd4a9640c4da396c572f87190af3a.png

b6167deec518417bb2df82d275fbf6c3.png

03b2e34e61ad424dad965ced5f9d7b12.png

        回到MigrationRuleHandler#doMigrate()方法,最终调用的是这里,结果如下:

cbcf6f358f7345c0839848a57893e62b.png

0f0231940c6f47fd87564b24c4d755f2.png

        先看MigrationInvoker#refreshServiceDiscoveryInvoker()方法,代码如下:

d7d747e7741a46a09644a1a19f2f3295.png

 

c78a2c44566a4f5ebbd018c2c1d2f871.png

abbae783467148e1966aa6fc53961069.png

        因此调用registryFactory.getRegistry()方法为,得到的Registry对象是ListenerRegistryWrapper对象,而ListenerRegistryWrapper对象的registry属性是ServiceDiscoveryRegistry,断点验证:

539a6381cc7a4cec9a53970163cc0a86.png

        继续看RegistryProtocol#doCreateInvoker()方法,代码如下:

35070aa0e0654f83a73499e2f9b7e957.png

        先看看ServiceDiscoveryRegistryDirectory#buildRouterChain()方法,代码如下:

5f30974fde4c4e599ca639ed98dd15df.png

bb46066bc97948cd839a11a5966ed1d1.png

        重点看看是如何获取RouterFactory对象的集合的,ExtensionLoader#getActivateExtension()方法的代码如下:

b796a298c2a54645b33d0f4212cdcb15.png

3b9f6eead5794c5792285484fd939149.png

8ebee0d80aea4af2977db2ab905bf96d.png

        再看看org.apache.dubbo.rpc.cluster.RouterFactory中的内容,结果为:

182b6c70175a4235bbeabfd436eb5481.png

        最终遍历RouterFactory的集合,调用RouterFactory#getRouter()方法,得到Router对象,结果为:

b5ba0693931e42b49ecbed651e35af58.png

        回到RegistryProtocol#doCreateInvoker()方法,ServiceDiscoveryRegistryDirectory#subscribe()方法,代码如下:

aeb1926e7c9444e4942c49e7b79a53af.png

e7e138d247514b28b9d343ec46d24d3b.png

eea9f11f73c342528963e35ed3516886.png

a43aee45e82d40e8b7866ba7c337fd15.png

aa36c70d285a4f23b2214035696667ae.png

        看看ServiceDiscoveryRegistry#createServiceDiscovery()方法,代码如下:

7239be2532d14e91be7e0202cd03f70e.png

c4c57d29b872413cb155589c8527939d.png

        可知serviceDiscovery属性实际上是EventPublishingServiceDiscovery对象,而EventPublishingServiceDiscovery对象的serviceDiscovery属性是NacosServiceDiscovery对象。回到ServiceDiscoveryRegistry#subscribe()方法,代码如下:

3b70c8bb8d0b45fdb462a4cbfc4c0e74.png

6e068e980dcd4bcbae9e8021c1d94873.png

708c340f9ee24f71819170c38fa5a3c4.png

        回到MigrationInvoker#migrateToServiceDiscoveryInvoker()方法,到这里为止,发现MigrationInvoker#refreshServiceDiscoveryInvoker()方法,其实没做什么,那重点应该是MigrationInvoker#refreshInterfaceInvoker()方法,代码如下:

4041360075594da79b03845351f847a0.png

f8c65e5747eb41c9b2c41c7f2e99afdd.png

c8808739f1d04692a2c90ea9a262d864.png

b24ba9a2faff4ff68d0d3b8290f60b97.png

2e41685ac6d6430b8182cb258c48a191.png

4d7d0e7427474886a2283bfd2903c3f3.png

1af2366e9183409b98c65cf0b02553bf.png

        先看看父类的subscribe()方法,看了些啥,代码如下:

b86e3f40f0b3430a88404465a5159d26.png

        再看NacosRegistry#doSubscribe()方法,代码如下:

8bfe01f359c647eca9d73ebe33b79d6e.png

38732a51d8744e40b5c800295a2c4021.png

a9e3c5cfd8fd4dfa82c70f2e6989dae9.png

3ed3131e8db04e5fad239aaa1150a7bb.png

f4888c712c8c4ff8819ef9d103625611.png

        获取到服务名的列表之后,再调用NacosRegistry#doSubscribe()方法,订阅服务,如下:

3f19447d7869452ea14a0c7a5f193fb8.png

        然后遍历服务名列表,调用NacosRegistry#subscribeEventListener()方法,代码如下:

cc7d412ef69247e4af08bfd287f23aa3.png

        看看NacosRegistry#notifySubscriber()方法,代码如下:

5bd70d9a241b4008a14130f998b944e3.png

1cc52b32a4584ec3a671d6d662b6a2ed.png

3fedfe4251e04f8ca65a29d872c48664.png

7eb2672096f74e8b8c4f997198593661.png

        先看一下AbstractRegistry#saveProperties()方法,该方法的作用是将从注册中心获取的服务提供方的信息写到本地文件,如果注册中心挂了,就读取本地文件,代码如下:

0d2580f8b8bb4befaa65aa0f7bf0fd5c.png

f89adeba24df4e8a846ba07847b95c73.png

82bd6525d7c84f5babb478acbcad7b67.png

        其中,file是AbstractRegistry的属性,默认为如下:

c1a227052a8a42faaeef868bf0cfb6bc.png

        当然,如果服务发生变更,最终会调用到AbstractRegistry#notify()方法,再次更新本覅文件,病假版本号会+1。再看RegistryDirectory#notify()方法,代码如下:

2eb8e8c417a247e18ed3b6268c04b84f.png

f943f5b1ed7249aba0c0bb61e345c873.png

7df225f263574055a96ae247f790b225.png

        重点看看RegistryDirectory#toInvokers()方法,传入的URL对象当然是服务提供方的地址信息,代码如下:

e103ef7b6a7a45feaa93a55e17e6701e.png

993f393ecb054d33aad9948374528b4c.png        重点看protocol.refer()方法是如何生成Invoker对象的,实际上DynamicDirectory的protocol属性也是通过Setter方法设置的,与前面也讲过,生成DynamicDirectory对象的时候会进行属性的注入,因此最终protocol实际上是Protocol$Adaptive对象,也就是调用Protocol$Adaptive#refer()方法,代码如下:

dac0289c600d4b369152b8dae8c97227.png

        由前面可知,在Protocol$Adaptive#refer()方法中,由于传入的URL的协议是"dubbo",因此extension对象实际上是ProtocolFilterWrapper对象,而ProtocolFilterWrapper对象的protocol属性是ProtocolListenerWrapper对象,而ProtocolListenerWrapper对象的protocol属性是DubboProtocol对象。先看看ProtocolFilterWrapper#refer()方法,代码如下:

afaaf3791d1d416c8701000ed6e6c595.png

f6fd0d9fec47490384ebbfd0c4525342.png

        断点看看最终得到的FilterNode链是什么样的,结果如下:

d580356890224eddaca7e76ec7cba585.png

        回到ProtocolFilterWrapper#refer()方法,再调用ProtocolListenerWrapper#refer()方法,代码如下:

196cfbccb27c4ef79c6d86b04fdbb0a2.png

        最终调用的才是DubboProtocol#refer()方法,而refer()方法并不在DubboProtocol中实现,而是在其父类,因此最终调用的是AbstractProtocol#refer()方法,代码如下:

f3bd6c00b2fe48d5a24bb9f7cc8dbdbf.png

d5c9c5d4e0d1495d81a727838e33a38d.png

378b0f6378cc403ab32967368be19e45.png        这里的requestHandler跟之前服务提供是一样的,是共用的,继续看Exchangers#connect()方法,代码如下:

6db6471fd8e247a49a927a716746bd8e.png

bb2ec677d4a5460eb326dc637326a087.png

4686dd7e43a24c3280b278bf80270bb2.png

9715b8d7a57649baa120ba670c2be019.png

f58d46403e014c1eae0c4f497f548ad8.png

756755125eeb47ebb9ceb73c8112a0ba.png

5cc05c6bbf074543b573911251f8ac08.png

7b9e6268ede14eed885710cac9d03454.png

4f5b535d735a432783c5b76741008d41.png

c3101d7db25d42ee9e0ce7ed579c6abd.png

b0f7f81b736f46848cd6d26bd98ecdf9.png

        剩下的内容将在最后一篇博客中讲解,敬请期待~

 

 

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值