今天来简单做一下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. C++关闭同步流 ios::sync_with_stdio(false)

    说明:ios::sync_with_stdio(false) 1.这句语句是用来取消cin的同步,什么叫同步呢?就是iostream的缓冲跟stdio的同步.这就是为什么cin和cout比scanf和 ...

  2. maven项目变成web项目

    具体步骤如图所示: 第一步:建议一个Maven Webapp项目  第二步:右击项目,选择属性,找到project facets,点击tuntimes标签选择apache tomcat v6.0选中P ...

  3. NetCore项目实战篇05---添加Ocelot网关并集成identity server4认证

    今天来给我们的项目增加API网关,使用Ocelot. 它是系统暴露在外部的一个访问入口,这个有点像代理访问的家伙,就像一个公司的门卫承担着寻址.限制进入.安全检查.位置引导.等等功能.同时我们还要在网 ...

  4. 002_python的in,while else,格式化输出,逻辑运算符,int与bool转换,编码

    数据 1.什么是数据? x=10,10是我们要存储的数据 2.为何数据要分不同的类型 数据是用来表示状态的,不同的状态就应该用不同的类型的数据去表示 3.数据类型 数字 字符串 列表 元组 字典 集合 ...

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

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

  6. 面试总结:鹅厂Linux后台开发面试笔试C++知识点参考笔记

    文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 文章是由自己笔试面试腾讯的笔记整理而来,整理的时候又回顾了一遍,中间工 ...

  7. 移动端纯css超出盒子出现横向滚动条

    <!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...

  8. jQuery的插件和跨域、ajax

    1. 插件: 也称组件 什么是: 拥有专属的HTML,CSS和js的独立页面区域 为什么: 重用! 何时: 只要一个功能/区域可能被反复使用时 如何: 3个来源: 1. 官方插件:jquery ui ...

  9. linux常用命令---网络端口信息与进程管理

    进程管理 进程管理

  10. RBAC权限分配

    RABC:基于角色的权限访问控制(Role-Based Access Control) 一般在登录系统认证通过后,会先确定的该用户的操作权限,判断用户的后续操作是否合法! RABC至少需要三张表:用户 ...