手撕源码Dubbo工作原理&源码分析
Posted Java老僧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手撕源码Dubbo工作原理&源码分析相关的知识,希望对你有一定的参考价值。
接下来的几篇文章开始介绍Dubbo的源码,本章介绍Dubbo的启动原理。
4. Dubbo框架设计
官方文档:http://dubbo.apache.org/zh-cn/docs/dev/design.html
4.1 Business部分
在Business部分,只有一个层面:Service
对于Service层,只是提供一个ServiceInterface,再在Provider中写对应的ServiceImpl即可
也就是说,对于应用而言,到这里就足以了
下面的部分全部都是Dubbo的底层原理
4.2 RPC部分
自上往下,依次有:
config 配置层:对外配置接口
它是封装配置文件中的配置信息
以ServiceConfig, ReferenceConfig为中心
可以直接初始化配置类,也可以通过Spring解析配置生成配置类
proxy 服务代理层:服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton
它实际上就是辅助生成RPC的代理对象
以ServiceProx为中心,扩展接口为ProxyFactory
这一层就是注册中心的核心功能层(服务发现、服务注册)
以服务URL为中心,扩展接口为RegistryFactory, Registry, RegistryService
cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心
这一层的调用者Invoker可以保证多台服务器的服务调用,以及实现负载均衡
以Invoker为中心,扩展接口为Cluster, Directory, Router, LoadBalance
monitor 监控层:RPC调用次数和调用时间监控
以Statistics为中心,扩展接口为MonitorFactory, Monitor, MonitorService
protocol 远程调用层:封装RPC调用
这一层是RPC的调用核心
以Invocation, Result为中心,扩展接口为Protocol, Invoker, Exporter
4.3 Remoting部分
自上往下,依次有:
exchange 信息交换层:封装请求响应模式,同步转异步
这一层负责给服务提供方与服务消费方之间架起连接的管道
以Request, Response为中心,扩展接口为Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
transport 网络传输层:抽象mina和netty为统一接口
这一层负责真正的网络数据传输,Netty也就在这一层被封装
以Message为中心,扩展接口为Channel, Transporter, Client, Server, Codec
serialize 数据序列化层:可复用的一些工具
扩展接口为Serialization, ObjectInput, ObjectOutput, ThreadPool
5. Dubbo启动流程
因为Dubbo的配置文件实际是Spring的配置文件
而Spring解析配置文件,最终都是回归到一个接口:BeanDefinitionParser
那就意味着,Dubbo肯定也提供了对应的BeanDefinitionParser:DubboBeanDefinitionParser
在BeanDefinitionParser中只定义了一个方法:
BeanDefinition parse(Element element, ParserContext parserContext);
下面剖析IOC容器启动时标签的解析机制
5.1 Dubbo的标签解析机制
5.1.1 进入parse方法
可以看到parse方法仅有一句:
1public BeanDefinition parse(Element element, ParserContext parserContext) {
2 return parse(element, parserContext, beanClass, required);
3}
注意这里的beanClass是成员变量而不是参数!
那难道说,每次解析标签,都是一个全新的DubboBeanDefinitionParser?
5.1.2 自行声明的parse方法
由于该方法太长,只取关键部分
1if (ProtocolConfig.class.equals(beanClass)) {
2 for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
3 BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
4 PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
5 if (property != null) {
6 Object value = property.getValue();
7 if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
8 definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
9 }
10 }
11 }
12} else if (ServiceBean.class.equals(beanClass)) {
13 String className = element.getAttribute("class");
14 if (className != null && className.length() > 0) {
15 RootBeanDefinition classDefinition = new RootBeanDefinition();
16 classDefinition.setBeanClass(ReflectUtils.forName(className));
17 classDefinition.setLazyInit(false);
18 parseProperties(element.getChildNodes(), classDefinition);
19 beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
20 }
21} else if (ProviderConfig.class.equals(beanClass)) {
22 parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
23} else if (ConsumerConfig.class.equals(beanClass)) {
24 parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
25}
这一部分在判断beanClass的类型!
可Dubbo的配置文件中并没有写class=…之类的内容,它怎么知道的?
那既然不知道beanClass从哪儿来的,就需要追到构造方法中了
5.1.3 DubboBeanDefinitionParser的构造方法
1public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
2 this.beanClass = beanClass;
3 this.required = required;
4}
构造方法很简单,但这个Class的参数从哪儿来的?
5.1.4 追溯到创建DubboBeanDefinitionParser的前一步
通过Debug可以发现,在这一步之前,还有一个调用过程,是调用了一个叫DubboNamespaceHandler的init方法
1public void init() {
2 registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
3 registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
4 registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
5 registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
6 registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
7 registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
8 registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
9 registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
10 registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
11 registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
12}
从这个方法中,可以看到,这是把Dubbo配置文件中可能出现的标签,全部转化为DubboBeanDefinitionParser了
也就是说,并不是解析一个标签就创建一个DubboBeanDefinitionParser,而是把所有可能出现的标签全部穷举了,创建出对应的DubboBeanDefinitionParser,这样遇到哪个标签,就使用哪个标签的解析器而已
而且注意到每次传入的Class对象,要么是xxxConfig,要么是xxxBean
xxxConfig是标签中的配置信息,只能出现一次
xxxBean是发布的服务/引用的服务,每组标签都会创建一个Bean
对于xxxConfig,只是把配置封装到对应的Config类里就可以了
但提供方发布的服务、消费方的服务引用,是另外一种机制。
下一篇文章会介绍Dubbo服务的发布原理,请持续关注。。。
觉得文章还不错的话,麻烦您点个关注哦~
点击“阅读原文”
以上是关于手撕源码Dubbo工作原理&源码分析的主要内容,如果未能解决你的问题,请参考以下文章