Dubbo学习-源码学习

时间:2022-03-30 21:12:59

Dubbo概述

dubbo框架提供多协议远程调用,服务提供方可以是分布式部署。dubbo框架可以很简单的帮我们实现微服务。

此处援引官网上图片

Dubbo学习-源码学习

dubbo分为客户端和服务提供方

服务方将服务注册到注册中心

客户端从注册中心获取已注册服务访问方式

客户端通过指定协议访问服务提供方

根据dubbo架构,源码分析我们主要切入点是:

  1. dubbo配置如何生效
  2. 客户端如何调用服务
  3. 注册的服务如何保证被调用到
  4. dubbo远程调用的协议如何工作

针对以上我们来分析Dubbo源码:

Dubbo源码是maven管理,工程主要由dubbo-config,dubbo-container,dubbo-filter,dubbo-monitor,dubbo-registry,dubbo-remoting,dubbo-rpc,dubbo-serialization组成。

Dubbo学习-源码学习

  • dubbo-config :dubbo配置文件相关模块
  • dubbo-container :dubbo依赖的上下文
  • dubbo-filter : dubbo支持的请求过滤器
  • dubbo-monitor : dubbo监控管理模块
  • dubbo-registry : dubbo使用注册中心相关的模块
  • dubbo-remoting : dubbo整个远程交互框架,如何将通道、序列化、通信载体有机整合
  • dubbo-rpc : dubbo远程调用协议实现。
  • dubbo-serialization : 远程通信中序列化实现

dubbo配置加载

dubbo配置是以松耦合的方式嵌入到spring里面,因此说到dubbo配置加载就必然和spring扯不开关系。

从dubbo配置文件的命名空间说起,我们通常在配置dubbo服务时要在spring配置基础上添加dubbo的命名空间(xmlns)和模板:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

  

xmlns:dubbo的值指定了dubbo配置命名空间。
spring中不同的命名空间对应不同的配置解析器,那么dubbo的命名空间http://code.alibabatech.com/schema/dubbo也就对应着自己的解析器。
spring维护着命名空间和对应解析器的关系,这些对应关系包括spring内置的和第三方定制关系

spring命名空间

spring启动中解析dubbo xml文件时首先获取跟节点中的所有xmlns值,根据配置的命名空间从命名空间管理器中获取该空间对应的解析器对象

命名空间管理器会从classpath下META-INF/下加载并解析所有的所有spring.handlers命名的文件,该文件以键值对配置了命名空间和对应解析器。

dubbo的命名spring.handler配置如下:

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

路径:META-INF/dubbo/spring.handler

如此,spring在加载dubbo配置时会调用dubbo命名空间解析器DubboNamespaceHandler来解析配置文件,dubbo的入口也就是DubboNamespaceHandler。

dubbo配置解析处理器(DubboNamespaceHandler)

dubbo配置解析处理器就是命名空间配置的com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,该处理器作为dubbo对spring的扩展完成dubbo自定义的配置解析工作。spring的命名空间处理器扩展点是:org.springframework.beans.factory.xml.NamespaceHandlerSupport,DubboNamespaceHandler完成dubbo内部所有解析器初始和加载。解析器包括application节点解析、module节点解析、registry、monitor、provider、consumer、protocol、service、reference、annotation。

Dubbo学习-源码学习

这些解析器初始化代码如下:

 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 DubboBeanDefinitionParser(AnnotationBean.class, true));
}

DubboNamespaceHandler做了什么

spring加载配置文件过程中,读取到一个xml节点时,会将节点信息传递给DubboNamespaceHandler,handler获取节点的LocalName(在一个命名空间下去掉前缀剩下的节点名          称),根据名称从DubboNamespaceHandler获取对应的Parser,如:LocalName是application,就会获取到Handler初始化时注册的application对应的                                                    DubboBeanDefinitionParser对象,并调用该解析器的parse方法将节点转换成spring的RootBeanDefinition对象,对象保存了创建一个bean对象所需的所有信 息。

Dubbo服务(ServiceBean)

通过registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));注册的DubboBeanDefinitionParser

创建出的RootBeanDefinition最终被spring实例化成ServiceBean,ServiceBean即Dubbo实际的服务实例。实例化结束后,ServiceBean会从spring容器中获取配置的Provider信         息、application、module、registry、monitor、protocol等配置信息并设置到ServiceBean,设置完成后将服务自身暴露出去,如果本次暴露失败或者没有暴露,则在spring容器启       动完成后会再进行一次暴露。

服务暴露

服务暴露通过调用ServiceBean的export方法,通过注册中心,最终将服务实例以指定的协议暴露给调用方。

准备暴露路径

服务暴露前需要组织自己的访问路径和协议信息,只有有了这些信息,才能确定以何种协议被访问,以及通过什么路径被访问。组织访问信息的方法是服务自身的doExportUrls

ServiceConfig.doExportUrls()

     private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}

该方法调用了doExportUrlsFor1Protocol方法将协议和注册路径具体的组装起来,形成dubbo的访问路径,将访问路径包装成可被远程调用的调用器invoker。

invoker的生成代码如下:

             //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露本地服务)
if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (registryURLs != null && registryURLs.size() > 0
&& url.getParameter("register", true)) {
for (URL registryURL : registryURLs) {
url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
} else {
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
}
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
参数ref指向的是dubbo:service配置中ref中指定的服务对象,interfaceClass是该服务的接口,第三个参数则是该服务的访问路径。
Exporter<?> exporter = protocol.export(invoker);   // 最终将invoker暴露