本篇了解一下dubbo的服务发布流程
一. 从配置文件走起
这个xml文件是dubbo提供的一个demo,用过dubbo的同学应该都了解这就是暴露一个接口demoService
,那么这个接口如何暴露出去的呢?
既然是配置了这个xml就能暴露出去,那么肯定是从xml文件的配置开始,上图圈出来的红框中,很明显看出来是用的schema
定义的xml文件,不了解的同学可以百度一下:schema自定义配置
这个关键词,了解下schema自定义配置相关知识,这里就不再讲了,提供一个参考链接:https://www.cnblogs.com/jifeng/archive/2011/09/14/2176599.html
那么了解了schema自定义配置之后,就可以找到关于
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>
这行代码具体怎么开始了
上面这两个图表示了service
标签的定义
在org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#init
中可以看到对service
标签的解析器:new DubboBeanDefinitionParser(ServiceBean.class, true)
,所以我们进入org.apache.dubbo.config.spring.ServiceBean
里面查看源码
二. ServiceBean
查看ServiceBean源码
上面这些实现的接口都是spring的接口,其实主要作用就是保存了spring容器的引用,并且监听了spring容器的启动事件,再看看ServiceBean
的关系图
在ServiceBean
中有一个方法
他会发出一个Export事件
从注释可以发现是调用了org.apache.dubbo.config.spring.ServiceBean#exported
,这个方法在上面截图有了,里面调用了父类的org.apache.dubbo.config.ServiceConfig#exported
可以看到依旧是发布了一个事件,这个事件点进去看:
实际是走到了org.apache.dubbo.config.ServiceConfig#export
上面这里和dubbo老版本的区别很大,都走了事件发布和监听,比较绕,我也没理解太懂,不用管太多,只要找到org.apache.dubbo.config.ServiceConfig#export
这里,其实是真正开始发布服务,我们重点关注里面的方法:org.apache.dubbo.config.ServiceConfig#doExport
在doExportUrls
方法中,首先是加载配置文件中dubbo.registry.address
的注册中心信息并组装到registryURLs
,然后下面是一个for循环,这个循环的protocols
是代表通信协议,for循环表示一个服务可以有多个通信协议,比如tcp、http,默认是dubbo的tcp协议
然后进入:org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
这个方法,这个代码很多,前面都是各种校验和组装信息
重点在这里,可以看到注释写的是暴露本地服务和暴露远程服务,本地服务指的就是本地服务JVM里面的服务,不用通过zookeeper进行远程网络通信调用;那么远程服务就是通过zookeeper网络通信调用,暴露给可以给其他服务调用,这个应该不难理解
那么我们来看暴露本地服务的流程:
首先PROTOCOL
和PROXY_FACTORY
分别都是从SPI中获取的,getAdaptiveExtension
说明是获取的是个自适应的扩展类,那么分别看org.apache.dubbo.rpc.Protocol
和org.apache.dubbo.rpc.ProxyFactory
,这两个接口的@Adaptive
注解都是标注在接口上,说明是动态生成的扩展类(这块是SPI的知识,不了解的可以看我之前的文章:dubbo的SPI)
其中ProxyFactory
的作用就是为了获取一个接口的代理类,例如获取一个远程接口的代理。ProxyFactory
有2个方法,
-
getInvoker
:针对server端,将服务对象,如DemoServiceImpl包装成一个Invoker对象。 -
getProxy
:针对client端,创建接口的代理对象,例如DemoService的接口。
然后Protocol
的默认实现是@SPI("dubbo")
-> org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
ProxyFactory
的默认实现是@SPI("javassist")
->org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory
回到org.apache.dubbo.config.ServiceConfig#exportLocal
方法中,看方法调用PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local)
,其实调用的就是org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getInvoker
进入这个方法,首先看到是获取一个Wrapper
,这个wrapper是从org.apache.dubbo.common.bytecode.Wrapper#makeWrapper
里面获取来的,那么这个Wrapper
的作用其实是类似spring
的BeanWrapper
的,spring 的BeanWrapper
作用是:
BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器,由于BeanWrapper接口是PropertyAccessor的子接口,因此其也可以设置以及访问被包装对象的属性值。BeanWrapper大部分情况下是在spring ioc内部进行使用,通过BeanWrapper,spring ioc容器可以用统一的方式来访问bean的属性
那么dubbo的Wrapper其实和他差不多,在dubbo里面它的作用也是:包装一个接口或者类,可以通过Wrapper对接口或者类的实例进行取值、赋值、或者制定方法的调用。
回到代码中,拿到这个Wrapper包装类之后,new AbstractProxyInvoker<T>(proxy, type, url)
创建了一个Invoker
,这个Invoker
它是一个可执行的对象,能够根据方法的名称、参数得到相应的执行结果,它里面有一个很重要的方法:org.apache.dubbo.rpc.Invoker#invoke(Invocation invocation)
,这个方法的入参Invocation
代表了要执行的方法和参数等重要信息,Invoker在dubbo里面相当的重要,这里先说一下概念:
Invoker有3种类型的Invoker
-
本地执行类的Invoker
server端:要执行 demoService.sayHello,就通过InjvmExporter来进行反射执行demoService.sayHello就可以了。 -
远程通信类的Invoker
client端:要执行 demoService.sayHello,它封装了DubboInvoker进行远程通信,发送要执行的接口给server端。
server端:采用了AbstractProxyInvoker执行了DemoServiceImpl.sayHello,然后将执行结果返回发送给client. -
多个远程通信执行类的Invoker聚合成集群版的Invoker
client端:要执行 demoService.sayHello,就要通过AbstractClusterInvoker来进行负载均衡,DubboInvoker进行远程通信,发送要执行的接口给server端。
server端:采用了AbstractProxyInvoker执行了DemoServiceImpl.sayHello,然后将执行结果返回发送给client.