vlambda博客
学习文章列表

【手撕源码】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    }
12else 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    }
21else if (ProviderConfig.class.equals(beanClass)) {
22    parseNested(element, parserContext, ServiceBean.class, true"service""provider", id, beanDefinition);
23else 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工作原理&源码分析(二)【手撕源码】Dubbo工作原理&源码分析(二)

觉得文章还不错的话,麻烦您点个关注哦~




点击“阅读原文”