dubbo源码分析5(dubbo服务暴露入口)

  经过了前面这么多的铺垫,没错,前面说了这么一大堆的都是铺垫,我们说了spi,以及基于spring的扩展,这一篇就开始说说dubbo吧!

1.dubbo的配置文件解析

  在第一篇的时候,我们运行了dubbo2.7.5(注意版本)的源码中的demo,我们可以看看服务提供者的配置文件,下面红色框框中应该不陌生了吧,嘿嘿( ̄▽ ̄)ノ

 

  然后我们上一篇中说的原理,去找到对应的命名空间处理器,下图所示;

  所以我们就能知道命名空间处理器是DubboNamespaceHandler,而且我们还能看到一些有意思的东西,由于dubbo已经被阿里给贡献到开源社区apache了,所以这里还保留了之前ali的命名空间,我们也能继续使用,嘿嘿

  自己有兴趣的话,可以自己看看下图中的spring.schemas文件以及对应的xsd文件了,看看这个xsd文件就能知道dubbo的每个标签都有些什么属性了(我表示没有这么大的兴趣๑乛◡乛๑,你们自己看,哈哈哈)

 

  继续看看这个DubboNamespaceHandler中,下图所示,我们随便看一秒钟就能知道dubbo的配置文件中,跟dubbo相关的标签就是这么14个,而且解析每个标签的解析起都是DubboBeanDefinitionParser去解析成BeanDefinition对象,只不过每个BeanDefinition里面封装的对象不一样罢了

 

  这么多标签我们说哪一个呢?先说说我最感兴趣的一个标签<dubbo:service />, 大家知道这个标签是干啥的么,咱们后面慢慢看

 

2.分布式服务基础

  在说明<dubbo:service />标签的作用之前,我们先说说为什么要有远程调用?或者说dubbo在分布式架构中使用的位置,下面就用于给萌新科普一下,嘿嘿~

  下图所示是我们看到的一个很经典的后端MVC三层结构, 这个应该都很熟悉了,而且这里后端的那三层都是在同一个机器上;

 

  那么问题来了,如果我要将service层放在另外一台机器上,你觉得Controller调用Service怎么做会比较好?下面有两种方式

  方式一:我们在另外一台机器B上搭建一套MVC结构,注意,此时的机器B上的Controller中没有任何逻辑代码,就只是单纯的暴露一个机器B的入口,所以下面的步骤2可以直接使用Http请求去调用,比如使用Feign,HttpClient等http工具,聪明的人已经知道了,这特喵的就是springcloud的远程调用的方式嘛(╯-╰)/

 

  方式二,我们在机器B中,完全不需要Controller层,不就是两台机器通信么?大家知道java的网络编程吧,直接使用Socket建立网络通信就ok了;

  我们现在使用dubbo就是用于实现下面的步骤2,更通俗的理解就是封装了java的网络编程(其实就是封装了netty框架),使得两台服务器之间可以进行远程调用,而不需要使用http请求的方式了;

 

  方式2优化: 但是由于我们的机器B可能有很多台,我们总不能在机器A中每次调用B机器的接口的时候,都要知道B的ip和端口吧?那多坑爹呀!

  所以我们在机器B1和机器B2启动的时候,需要将机器B1和机器B2的接口信息(ip+端口+接口+方法+版本等信息)都放到注册中心(如下图的步骤1和步骤2),然后机器A从注册中心中将所有接口信息都给下载下(步骤3),然后决定去调用哪个机器上的服务(这里会涉及到负载均衡策略);

  所以前端调用机器A,机器A根据调用的接口就可以找到对应接口所在的机器是B2(步骤5),然后通过网络编程去连接机器B2就好了

  在这里机器A就是服务消费者,机器B1和机器B2就是服务提供者

 

3.dubbo服务提供者暴露服务入口

  在上面我们知道了,dubbo其实就是解决一台机器去远程调用另外一台机器的接口,而且还将服务暴露到注册中心里,并提供负载均衡策略 的远程调用框架;

  那么在dubbo中什么叫做服务呀?我们看看服务提供者的配置文件,下图所示一个<dubbo:service>标签就对应着一个服务接口,这个接口的信息需要暴露到注册中心的;

  业余小知识:可以思考一下,下图中这个接口只会暴露到注册中心中去么?(肯定是暴露到注册中心中一份,也会暴露一份到当前所在机器的jvm中呀!为什么呢?因为也有可能当前机器中其他的服务会使用到这个demoService服务呀,同一台机器上就不必走注册中心,肯定直接走jvm会更快呀)

  所以我们又可以知道暴露服务,可以分为本地暴露和远程暴露嘛!当然,我们主要看的是远程暴露吧,哈哈哈哈๑乛◡乛๑

 

  下面我们就看看是怎么解析这个<dubbo:service>标签的过程

  3.1.DubboNamespaceHandler

  这里就是缓存了dubbo每一个标签所对应的一个解析器,当实际去解析<dubbo:service>标签的时候,就会从缓存中获取解析起,new DubboBeanDefinitionParser(ServiceBean.class, true),然后调用这个解析起的parse方法

  这里,继续科普一下,在spring中的BeanDefinition的作用:对xml文件中每一个bean的一个统一的抽象,简单来说就是首先会将所有解析的bean都变成BeanDefinition<T>对象,然后后面统一对所有的BeanDefinition进行实例化真正对象T,可以看看这个老哥博客的图,很清晰,嘿嘿(╯-╰)/

 

  3.2  DubboBeanDefinitionParser的parse方法

  这个方法很长,我们只看解析ServiceBean的地方

  在看这个方法之前,继续强调一点,这里只是封装了BeanDefinition<ServiceBean>这种对象,在spring的机制中,后续才会真正的根据BeanDefinition去实例化出ServiceBean对象的(你要问spring中到底是哪里会实例化的,这个要自己去学习了,这里只是粗略的带过一下,这里重点还是dubbo)

  看到这里,暂时不往后看了,后面一堆逻辑容易头晕,趁着头脑还很冷静, 我们看点清神醒脑的

 

  3.3.实例化ServiceBean对象

  在spring的后续初始化流程中,会对所有BeanDefinition中封装的实际的对象进行实例化,其中一个就是ServcieBean,我们看看ServiceBean是个什么东东( ̄o ̄) . z Z

  这里请注意dubbo2.7.5之前和2.7.5之后的版本,变化很大很大,网上很多的ServcieBean的解析的博客都是2.7.5之前的,远古版本的了,我也在试着查了一些资料,尝试总结一下吧o(︶︿︶)o

  3.3.1 先看看2.7.5之前

  我把代码图和类的继承图都给出来,这里简单说一下spring中的原理,就不仔细看源码了
  实现了InitializingBean和ApplicationListener接口的bean实例化的时候会做的事情,就是一个Bean首先在xml中进行配置,然后spring的容器在初始化的时候,会首先经过加载变成BeanDefinition对象,然后进行实例化BeanDefinition中实际的类型,这里实际的类型就是ServiceBean,在创建了一个ServiceBean对象之后,设置属性值,然后就是对ServiceBean进行初始化操作:会判断是否实现了InitializingBean接口,实现了的话,就会执行afterPropertiesSet方法先初始化一下,然后会判断ServcieBean有没有初始化方法,就是配置文件中<bean>标签指定的init-method方法,  执行init-method方法之后,后续还有可能执行其他的各种对bean和容器的初始化操作,在经过了九九八十一难之后,ioc容器和bean都会初始化完毕,此时ioc容器会判断所有实例化之后的bean有没有实现ApplicationListener接口,进而去执行每个bean的onApplicationEvent方法,此时ServiceBean就会去执行onApplicationEvent()方法,实际的去开始暴露自己服务

 

 

  

  3.3.2 dubbo2.7.5的版本

  我们看看代码和类图,很明显已经看不到那个EventListener接口了,那么我们就要看看这里是怎么暴露服务的

 

 

 

  dubbo2.7.5提供了一个类DubboBootstrap类,这只是一个普通的监听器,但是里面有一个start方法,这里会调用exportServices()方法,然后就会去调用ServiceConfig的export()方法

(注意,serviceBean是ServiceConfig的一个子类),其实就可以看做会调用我们ServiceBean的export()方法,进行服务的暴露

 

 

 

  那么我们再看看从哪里调用的这个DubboBootstrap的start()方法,那么我们就知道所有流程了,下面再贴几段代码

 

 

 

 

  我们理一下思路,其实spring的初始化流程跟2.7.5之前是一样的,只不过在最后ioc和bean初始化完成之后,spring容器开始发布事件,在ioc容器中所有的实现了ApplicationListener接口的bean,首先会执行OneTimeExecutionApplicationContextEventListener的onApplicationEvent()方法,在这个方法里面再执行DubboBootstrapApplicationListener的onApplicationContextEvent()方法,在这方法里面会执行当前类的onContextRefreshedEvent()方法,最后就是执行dubboBootstrap.start();开始暴露服务

 

4.总结

   这篇博客其实就是说了我们从哪个角度去切入到dubbo框架中,说的东西很少,但是非常非常重要,只有找到了切入点之后,我们才可以痛快的继续往后走,不然学了一下就忘了,特喵的,以后每次都要看一遍,多麻烦呀!

  只要跟着我这里往后走,以后想看哪里点哪里,就不用百度了,哈哈哈哈

  其实本篇要说的最重要的就是dubbo的2.7.5版本,与之前相比暴露服务的方式做了很大的改变,使用到了一个DubboBootstarp启动器,在spring容器初始化完成之后,会发布对应的事件然后会带着dubbo的启动器也会跑起来,从中我们也可以看出来spring的扩展性是真的厉害,dubbo也设计的非常巧妙,哈哈哈哈

  下一篇真正的看看暴露服务的逻辑吧( ̄▽ ̄)ノ

posted @ 2021-12-12 16:24  java小新人  阅读(350)  评论(0编辑  收藏  举报