dubbo源码_dubbo--服务发现源码分析

一. 起源

Spring IOC中bean的生成分为两个阶段,一个是实例化阶段,另一个是初始化阶段。实例化阶段是生成将class变成对象的过程,以及参与一部分成员赋值,在这一过程中,可以通过InstantiationAwareBeanPostProcessor进行干预处理。另一个阶段主要是赋值,调用初始化方法等,在这一阶段,可以通过BeanPostProcessor对Bean做一些操作,例如@ Autowired和@Resource等就是这一阶段处理的。

当一个Bean初始化时,如果这个Bean中有@Reference(即需要注入dubbo的代理类),dubbo就会调用ReferenceAnnotationBeanPostProcessor对Bean的生成进行干预,ReferenceAnnotationBeanPostProcessor继承了InstantiationAwareBeanPostProcessorAdapter,是InstantiationAwareBeanPostProcessor的子子子类,Sping会调用该处理器的postProcessPropertyValues方法,方法的入参包含Bean的信息,再通过inject方法注入:

@Override

然后通过层层调用到达buildReferenceBean方法,在这个方法里开始构建ReferenceBean,ReferenceBean是服务发现的核心类:

private 

build方法的代码:

public 

configureBean方法的代码:

protected 

configureBean就是为ReferenceBean做一些参数配置,重点在最后一行的postConfigureBean方法:

@Override

postConfigureBean的前三行任然是对ReferenceBean的参数,最后一行调用了ReferenceBean的afterPropertiesSet的方法,正式开启服务发现。

ReferenceBean实现了FactoryBean,ApplicationContextAware,InitializingBean和DisposableBean接口,这些接口都是Spring IOC对Bean管理所使用的接口,实际上ReferenceBean的实例化对象并不归Spring IOC管,一个ReferenceBean对应一个dubbo服务类,Spring也管不了,ReferenceBean的接口逻辑都是dubbo代码支撑起的实现,而不是Spring,dubbo源码中对ReferenceBean的实例化对象也叫bean,不清楚的人很容易就理解为Spring所管理的,特此说明。

二. #afterPropertiesSet开启服务发现

afterPropertiesSet方法主要是想ApplicationContext拿取配置,如果ApplicationContext中没有,就使用默认的,然后调用getObject()方法进一步处理,再经过get()方法最终调用init()方法,init()方法会生成ReferenceBean中变量名为ref的成员变量,ref这个成员变量正是@Re ference注解注入的动态代理对象,所以ref的生成就十分重要,下面是init()方法源码:

/**

上面的代码非常长,但是逻辑十分清晰,这段代码可分为两段,第一段不难发现map是一个配置容器,这段代码的大部分操作都是向map中添加配置,第二段则是使用这个配置map调用createProxy方法创建代理类,继续深入createProxy方法:

private 

createProxy的方法稍微复杂一点,首先判断该引用是不是jvm中的,如果是就直接创建Invoker,其次判断用户是否指定的URL,如果未指定,就加载注册中心的URL,这一阶段的主要目的是拿到URL,然后再判断URL是不是有多个,如果只有一个,就调用refprotocol创建Invoker,如果有多个,就调用cluster的join方法合并多个Invoker为一个Invoker,然后检查,最终调用proxyFactory的getProxy方法生成ref,可以明显看出,大部分逻辑都是如何生成Invoker,这里面有几个细节需要继续深入,一个是refprotocol的ref方法,另一个是cluster的join方法,最后一个是proxyFactory的getProxy方法。流程图如下:

1a59f63c8b40297536a07b83127263af.png
服务发现流程图
  1. 深入refprotocol#ref

由于ReferenceBean中引入的refprotocol是由SPI机制生成的动态代理类,想要看到具体的代码,有两种办法,第一种是断点ExtensionLoader的createAdaptiveExtensionClassCode方法,查看生成的code,第二种是用Arthas反编译,这里选用第二种。

启动Arthas,先用sc搜索对应的class:

006e6a02733a9649d6a78f4847f92d56.png

红框中的类就是想要反编译的,然后再用命令jad com.alibaba.dubbo.rpc.Protocol$Adaptive进行反编译,得到的代码如下:

public 

重点关注refer方法,这个方法从url中取协议,如果协议为空,则默认为dubbo,然后用ExtensionLoader加载指定的拓展,然后再次debug这个getExtension方法,发现得到的Protocol是一个套娃结构,以zookeeper为注册中心时的registry为例:

6c890c3dd0f9286fbede7574ef53686f.png

最外层是ProtocolFilterWrapper,然后是QosProtocolWrapper,然后是ProtocolListennerWrapper,最后才是RegistryProtocol。

ProtocolFilterWrapper的作用是啥呢?我们在服务调动的时候可以有Filter对服务进行拦截处理,例如sofa-tracer对服务调用时,生产者和消费者打印日志中的trace_id一致,就是用的拦截器对消费者和生成都做了拦截,下面是ProtocolFilterWrapper的源码:

private 

QosProtocolWrapper是什么呢,QosProtocolWrapper是用来启动QosServer的,Qos全程Quality of Service,Qos详细的资料可以看这里,QosProtocolWrapper源码如下:

private 

ProtocolListennerWrapper又是干嘛的呢?在服务运行的过程中,有时候需要监听某个服务,在它暴露或发现它做一些事情,ProtocolListennerWrapper就是用来实现这个功能的,源码如下,当服务被暴露或发现服务时,会通知所有的listeners:

@Override
    

最后终于终于到了RegistryProtocol,程序运行到RegistryProtocol的refer方法,获取对应的注册中心,然后调用方法doRefer,在doRefer方法中new了一个RegistryDirectory对象,然后使用cluster#join方法生成Invoker,还记得有多个注册中心URL的时候怎么处理的吗,都是调用的这个方法,在下面我们深入cluster的join方法,下面是doRefer源码:

private 

2. 深入cluster#join

cluster也是一个Adaptive的动态代理类,再次通过Arthas反编译获取起代码如下:

/*

可以得知该动态代理类先是获取url中cluster的值,如果为空,则默认为failover,然后再通过getExtension获取具体的Cluster对象,在此通过断点获知Cluster也是个套娃:

4d582d31317ba6ae9106fcbc9898887b.png

外层是一个MockClusterWrapper,然后再是FailoverCluster。

MockClusterWrapper中将其包装成MockClusterInvoker:

public 

FailoverCluster中将其包装成FailoverClusterInvoker

public 

也就是说最终得到一个这样的数据结构,MockClusterWrapper对象中有directory和FailoverClusterInvoker对象两个成员变量,而FailoverClusterInvoker中有成员变量directory。

3. 深入proxyFactory#getProxy

再次通过Arthas反编译proxyFactory#getProxy得到源码如下:

public 

再次断点getExtension,又得到一个套娃结构:

2835cb8f92ec7a871372268e69c41844.png

进入StubProxyFactoryWrapper,getProxy方法如下:

@Override

这段代码的逻辑是使用JavassistProxyFactory生成代理类,然后检查URL中有没有“stub”参数。如果存在,就导出存根服务。

JavassistProxyFactory创建代理的方法如下:

@Override
    

可知,创建爱你代理时又包了一层InvokerInvocationHandler,InvokerInvocationHandler源码如下:

public 

注意invoke方法,被动态代理的类的方法都会交给invoke方法来处理,InvokerInvocationHandler这个类将除Object外的方法都封装成RpcInvocation交给invoker来调用。

现在屡一下思路,首先服务暴露时,先生成URL,再根据URL来生成Directory,再根据cluster的injoin方法生成Invoker,然后再根据proxyFactory生成适配的代理类,而当我们调用dubbo服务的方法时,真正干活的是Invoker,现在反推Invoker一共封了多少层:

1.首先是创建动态代理类时封的一层InvokerInvocationHandler

2. 然后是cluster的injoin封的MockClusterInvoker和FailoverClusterInvoker

invoker的初步封装一共三层

三.总结

服务发现的过程就是通过各种途径创建Directory,然后通过cluster创建成Invoker,然后生成动态代理类,当调用一个dubbo服务时,实际上就是层层调用Invoker的方法,下一篇深入Invoker远程调用的过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值