分布式服务之Dubbo源码学习


RPC最明显的特征就是 可以让消费方像调用本地接口的方式那样,调用服务提供方提供的远程服务。而实现这个特征,最重要的就是通过动态代理方式。这也从侧面可以看出,动态代理在整个RPC框架的地位举足轻重。

动态代理在消费方的意义

我们通常在消费方引用远程服务时,通过@Reference注入对象,这个对象其实是代理对象,具体点就是调用远程服务的对象。是在创建并实例化消费方所在的服务时,在bean的生命周期中的populateBean中的ReferenceAnnotationBeanPostProcessor的postProcessPropertyValues完成代理对象的创建并注入的。
Q:这个ReferenceAnnotationBeanPostProcessor对象是怎么来的?
A:通过容器初始化阶段中的invokeBeanFactoryPostProcessors完成ReferenceAnnotationBeanPostProcessor的BeanDefinition的创建,并将BeanDefinition注册到IOC容器中去,然后在容器初始化过程中的registerBeanPostProcessors()这一阶段完成ReferenceAnnotationBeanPostProcessor对象的创建。
当要对@Reference标注的field进行注入对象时,通过ReferenceAnnotationBeanPostProcessor完成对象的创建,这个对象,默认是基于Javasist的代理对象。
Q:ReferenceAnnotationBeanPostProcessor的beanDefinition注入到IOC容器是怎么触发的?
A:有两种解析方式并注入到IOC容器中

  1. 通过Xml解析的方式
  2. 通过@EnableDubbo注解的方式,需要配合@Configuration使用【基于注解驱动的编程】。

通过XML的方式解析BeanDefinition注册

通过Spring Xml的解析方式(NamespaceHandler)来完成有关Dubbo的相关beanDefinition的解析和注册到IOC容器中去。
这个解析xml的过程就是将Dubbo标签解析成对应的实体类的BeanDefinition,每一个Dubbo标签被解析成对应的BeanDefinition都是一个独立的Bean对象
Q:Dubbo提供的服务是创建新的Bean对象,那么,新的Bean对象是怎么关联到原来的Spring对象?
A:如果是Xml的方式,dubbo:service有一个ref属性。注解的方式,也会通过ref设置进行Spring对象和Dubbo服务对象的关联,这一过程发生在ServiceAnnotationBeanPostProcessor中,大体流程是这样:先找到扫描包下的spring bean的BeanDefinition,然后再去注册ServiceBean的BeanDefinition,在这一阶段中,将原生的service服务的beanName值作为属性值赋值到ServiceBean的ref属性中去。

	BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class)
                .addConstructorArgValue(service)
                // References "ref" property to annotated-@Service Bean
                .addPropertyReference("ref", annotatedServiceBeanName)
                .addPropertyValue("interfaceClass", interfaceClass);

XML方式解析服务提供方和服务消费方

XmlBeanDefinitionReader解析spring的配置文件【IOC容器】触发
DubboNamespaceHandler,提供了解析dubbo相关标签的解析器,这个编码方式是基于表驱动的设计,来优化if-else判断
DubboBeanDefinitionParser,具体解析dubbo相关标签。

	@Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

DubboBeanDefinitionParser中的parse是具体解析每一个dubbo标签,通过传入其field域的beanClass,来区分解析的是哪一个标签并作适当处理,这个处理指的是将相关标签的属性值创建为RuntimeBeanReference,更好的与Spring运行环境进行集成,以便更好的能被注入inject。
默认情况下,dubbo:service被解析到IOC容器中,是以interface的全限定名,而Spring定义的bean默认是首字母小写的类名。如图所示。
在这里插入图片描述

通过Annotation注解的方式解析注册

通过注解的方式应用Dubbo,@EnableDubbo @Service @Reference。主要就是这三个供我们使用的注解。Dubbo中的一些其他的注解,比如@EnableDubboConfig和@DubboComponentScan,这些注解是基于注解驱动开发的必要条件。
明确一点,Dubbo提供的注解驱动的开发是基于Spring环境下。如果我们也想开发一个基于注解驱动的开发,在Spring环境下,一定要通过@Import注解引入实现了ImportBeanDefinitionRegistrar这个接口能注册BeanDefinition的类和实现该接口ImportSelector的类。
举例:Dubbo提供注解开发的

  1. 实现了ImportSelector的DubboConfigConfigurationSelector
  2. 实现了ImportBeanDefinitionRegistrar的DubboComponentScanRegistrar,在registerBeanDefinitions中,同时向Spring的IOC容器中注册了ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor这两种BeanPostProcessor的beanDefinition信息。
  3. Dubbo还提供了配置文件中配置的属性与Bean的绑定的注解,dubbo.application与ApplicationConfig等@EnableDubboConfigBindings。
  4. ApplicationConfig的beanDefinition信息,被IOC容器创建对象的时候,会被DubboConfigConfigurationBeanPostProcessor处理,会通过Spring的DataBind方式将propertyValues中的属性值赋值到ApplicationConfig bean中去。

ServiceAnnotationBeanPostProcessor与ReferenceAnnotationBeanPostProcessor

  1. ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor,是将Dubbo的@Service标注的类解析成有关ServiceBean的BeanDefinition
  2. ReferenceAnnotationBeanPostProcessor是一个BeanPostProcessor,是将消费方通过@Reference注解使用的服务方提供的API接口创建代理对象。

Dubbo与我司的RPC实现对比

Dubbo实现RPC框架更grateful,提供了两种使用RPC的方式xml解析和注解的方式;而我司的RPC是通过xml和注解结合使用的方式来提供RPC服务的,根本上来讲,只提供了XML的方式,而注解是服务于XML的。
两者共同之处都是将服务提供方的服务发布到ZK注册中心的时机都是在Spring的IOC容器初始化完成之后触发的,区别在于实现方式。
Dubbo:每一个dubbo:service或者其@Service都是一个独立的ApplicationListener【ServiceBean】
我司RPC:只提供一个ApplicationListener,RPC服务的发布中心,实现粗糙,在Spring容器中只有一个ApplicationListener对象。因此,在进行服务发布到ZK中去的时候,是从IOC容器中获取所有的Bean一起发布,ServicePublisher.publish(bean, targetClass);当发布的bean不是RPC服务时,报一个AnnotationNotFoundException,被其不做处理的捕捉。因此,这种方式,导致,在接口和实现类上都要有RPC的注解标识,以便能被消费方识别。

	catch (AnnotationNotFoundException var9) {
                        ;
    }

Apache Dubbo:有一个统一的监听Spring容器事件的监听器DubboBootstrapApplicationListener,思路跟RSF一样。不再是(Alibaba的)每一个ServiceBean都是一个监听Spring容器事件的监听器。

总结:

  1. 基于Spring环境下的注解驱动开发的三个要点:@Import(注册BeanDefinitionRegistar.class);实现ImportBeanDefinitionRegistar接口;实现ImportSelector接口。
  2. 看源码是十分重要,对源码的掌握程度,直接决定了代码质量,就比如我司的RPC框架,实现方式真的是很粗糙。猜想,Dubbo的优雅实现,至少精细看过两种源码,一种是Spring源码,另一种是mybatis-spring源码,因为MapperScannerConfigurer其实就是一个BeanDefinitionRegistryPostProcessor。在解决问题上,充分利用了Spring的RuntimeBeanReference,以及DataBinder和MetaData等。而我司仅仅是利用了Spring的NamespaceHandler。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值