今天来简单做一下Dubbo服务注册部分源码学习手记。

一、Dubbo配置解析

目前Dubbo最多的用法就是跟Spring集成,既然跟Spring集成,那么,Dubbo对象的实例化都将交由Spring统一处理。而Dubbo配置,对于Spring来说其实就是自定标签。这里Dubbo自定义标签解析类,在Dubbo配置模块(\dubbo-config\dubbo-config-spring\src\main\resources\META-INF/spring.handlers)进行了声明:

http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
 public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@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("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.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());
}
}

对应一下Dubbo服务提供方的配置文件:

<dubbo:application name="demo-provider"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<dubbo:protocol name="dubbo"/>
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>

就是将这些标签解析成ApplicationConfig...ServiceBean等等对象。而服务注册部分最主要的桥梁就在于ServiceBean初始化解析完毕的时候。有Spring容器调用ServiceBean.afterPropertiesSet方法:

 public void afterPropertiesSet() throws Exception {
// 此处省略配置检查
export();
}

二、配置检查与缺省填充

进入export方法中,首先会对ServiceBean进行发布前的配置检查与填充。

 public synchronized void export() {
checkAndUpdateSubConfigs(); // 加载并更新配置信息到Bean对象中,并检查
}

这里会有一系列的配置校验与配置填充的逻辑:

 public void checkAndUpdateSubConfigs() {
checkDefault();
if (provider != null) {
inheritIfAbsentFromProvider();
}
if (module != null) {
inheritIfAbsentFromModule();
}
if (application != null) {
inheritIfAbsentFromApplication();
}
checkApplication();
checkRegistry();
checkProtocol();
this.refresh();
checkMetadataReport();
checkRegistryDataConfig();
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
checkRef();
checkStubAndLocal(interfaceClass);
checkMock(interfaceClass);
}

这里只列了部分配置检查与填充。总体配置的区分是否配置中心优先,优先级为:

 public void refresh() {
// getPrefix为对应配置类的前缀,ProviderConfig->Provider, ServiceBean->Service,
// getId为beanId,
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
config.addProperties(getMetaData()); // Bean对象的初始值
if (Environment.getInstance().isConfigCenterFirst()) {
// -D系统属性 > 配置中心应用配置 > 配置中心全局配置 > Bean对象的初始值 > 属性文件中的信息
compositeConfiguration.addConfiguration(3, config);
} else {
// -D > Bean对象的初始值 > 配置中心应用配置 > 配置中心全局配置 > 属性文件中的信息
compositeConfiguration.addConfiguration(1, config);
}
}

三、获取和构建注册中心统一资源定位器

 private void doExportUrls() {
// 获取注册中心统一资源列表,如果注册中心没有初始化,则先初始化
List<URL> registryURLs = loadRegistries(true);
// 将目标服务所有协议模式注册到每一个注册中心上
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}

四、服务启动与服务注册

在doExportUrlsForProtocal方法里边其实就做了以下几件事:

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
Map map = builderUrl();
// 代表一个服务
URL url = new URL(name, host, port, (StringUtils.isEmpty(contextPath) ? "" : contextPath + "/") + path, map);
// 通过代理工厂将ref对象转化成invoker对象
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
//代理invoker对象
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用配置协议启动、暴露服务
Exporter<?> exporter = protocol.export(wrapperInvoker);
//一个服务可能有多个提供者,保存在一起
exporters.add(exporter);
}

这里,我们着重看第10行服务暴露部分,这里调用export方法,如果是Dubbo协议的话,分别列举一下DubboProtocal、HttpProtocol的export:

 public class DubboProtocol extends AbstractProtocol {
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
//忽略若干代码
//打开服务
openServer(url);
optimizeSerialization(url);
return exporter;
}
}
 public class HttpProtocol extends AbstractProxyProtocol {
protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
String addr = getAddr(url);
HttpServer server = serverMap.get(addr);
server = httpBinder.bind(url, new InternalHandler());
}
}

其实这块就是,根据不同协议分别做了服务启动,如果是Dubbo协议则启动NettyServer,如果是Http协议则启动一个Tomcat。

那么现在服务启动了,但是在哪里注册了呢?其实我们要关注这个protocol对象的构建:

 /**
* The {@link Protocol} implementation with adaptive functionality,it will be different in different scenarios.
* A particular {@link Protocol} implementation is determined by the protocol attribute in the {@link URL}.
* For example:
*
* <li>when the url is registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=dubbo-sample,
* then the protocol is <b>RegistryProtocol</b></li>
*
* <li>when the url is dubbo://224.5.6.7:1234/org.apache.dubbo.config.api.DemoService?application=dubbo-sample, then
* the protocol is <b>DubboProtocol</b></li>
* <p>
* Actually,when the {@link ExtensionLoader} init the {@link Protocol} instants,it will automatically wraps two
* layers, and eventually will get a <b>ProtocolFilterWrapper</b> or <b>ProtocolListenerWrapper</b>
*/
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

这段源码的意思是,在获取Protocol实例的时候,Dubbo框架自动给包装了两个切面,其实服务注册就是在这个切面里边完成的:

 public class ProtocolListenerWrapper implements Protocol {
private final Protocol protocol;
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//如果是registerProtocol,则调用RegisterProtocol.export方法
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
}

再看看RegisterProtocol的export:

 public class RegistryProtocol implements Protocol {
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
register(registryUrl, registeredProviderUrl); // 服务注册
}
}

如果注册中心是ZK的话其实就是给ZK写数据:

 @Override
public void register(URL url) {
//忽略很多代码
doRegister(url);
//忽略很多代码
}
protected void doRegister(URL url) {
try {
zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}

相关文章:https://www.jianshu.com/p/7f3871492c71

Dubbo源码笔记-服务注册的更多相关文章

  1. Dubbo源码(四) - 服务引用(消费者)

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 上一篇文章,讲了Dubbo的服务导出: Dubbo源码(三) - 服务导出(生产者) 本文,咱们 ...

  2. Dubbo源码(五) - 服务目录

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 今天,来聊聊Dubbo的服务目录(Directory).下面是官方文档对服务目录的定义: 服务目 ...

  3. Dubbo 源码分析 - 服务调用过程

    注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...

  4. dubbo源码之服务发布与注册

    服务端发布流程: dubbo 是基于 spring 配置来实现服务的发布的,对于dubbo 配置文件中看到的<dubbo:service>等标签都是服务发布的重要配置 ,对于这些提供可配置 ...

  5. Dubbo源码学习--服务是如何引用的

    ReferenceBean 跟服务引用一样,Dubbo的reference配置会被转成ReferenceBean类,ReferenceBean实现了InitializingBean接口,直接看afte ...

  6. Dubbo源码学习--服务是如何发布的

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 ServiceBean ServiceBean 实现ApplicationListener接口监听Conte ...

  7. Dubbo源码学习--服务发布(ServiceBean、ServiceConfig)

    前面讲过Dubbo SPI拓展机制,通过ExtensionLoader实现可插拔加载拓展,本节将接着分析Dubbo的服务发布过程. 以源码中dubbo-demo模块作为切入口一步步走进Dubbo源码. ...

  8. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  9. Dubbo 源码分析 - 服务导出

    1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可 ...

随机推荐

  1. github使用命令

    创建本地库,提交,和绑定github ,上传代码 git init git add README.md git commit -m "first commit" git remot ...

  2. STM32CubeMX 多通道 ADC DMA 配置 测试小程序

    要点: 1.STM32F103C8T6单片机 2.ADC+DMA 多通道 重点是ADC+DMA配置,ADC+DMA配置如下 其他配置略略略略. 然后各位自行直看.ioc文件,生成代码后在while之前 ...

  3. Java集合简单介绍

    再最前面分享一下我再学习集合时的方法: 1.首先了解各集合的定义和特点 2.集合的构造方法和常用方法(增删改查等) 3.了解集合使用的场景,再什么情况下使用什么类型的集合(关键是集合的特性) 4.了解 ...

  4. 异常: java.lang.ClassNotFoundException: org.springframework.web.util.IntrospectorCleanupListener

    如果出现这个错误信息,如果你的项目是Maven结构的,那么一般都是你的项目的Maven Dependencies没有添加到项目的编译路径下 解决办法: ①选中项目->右键Properties-& ...

  5. salesforce零基础学习(九十七)Event / Task 针对WhoId的浅谈

    我们在Sales Cloud中经常会创建顾客,如果针对TO C业务,会启用个人顾客,比如针对车企行业,有一些场景是需要卖给个人的,而不只是企业采购.当通过打电话或者其他的场景有潜在客户并且转换成客户以 ...

  6. Cookie什么?Cookie和Session防御怎么做?

    Cookie什么?Cookie和Session防御怎么做? Cookie的概念 Cookie,复数形态Cookies,中文名称为小型文本文件.指某些网站为了辨别用户身份.进行session跟踪而储存在 ...

  7. Java return 关键字

    一.基本概念 return一方面用在循环语句中来结束循环,另一方面用来终止函数的执行或者退出类的方法,并把控制权返回该方法的调用者.如果方法有返回类型,则return的返回该类型的值:如果没有返回值, ...

  8. Vue + Element-ui实现后台管理系统(2)---项目搭建 + ⾸⻚布局实现

    项目搭建 + ⾸⻚布局实现 上篇对该项目做了个总述 :Vue + Element-ui实现后台管理系统(1) --- 总述 这篇主要讲解 项目搭建 + 后台⾸⻚布局实现 : 整体效果 后台首页按布局一 ...

  9. Try-Catch包裹的代码异常后,竟然导致了产线事务回滚!

    导读:​一段被try-catch包裹后的代码在产线稳定运行了200天后忽然发生了异常,而这个异常竟然导致了产线事务回滚.这期间究竟发生了什么?日常在项目过程中该如何避免事务异常?就在这个时候,老板拿着 ...

  10. 【Hadoop离线基础总结】impala简单介绍及安装部署

    目录 impala的简单介绍 概述 优点 缺点 impala和Hive的关系 impala如何和CDH一起工作 impala的架构及查询计划 impala/hive/spark 对比 impala的安 ...