本章我们将分析一下consumer向注册中心注册,并获取服务端相应的信息,根据这些信息生产代理对象的过程和源码。

1.类图

上图展示了部分消费者注册及生成代理对象过程中需要使用到的类和接口,其中:

spring适配涉及到的类:DubboNamespaceHandler、DubboBeanDefinitionParser、ReferenceBean;

配置信息存储:ReferenceConfig、RegistryConfig、MonitorConfig、ProtocolConfig、ConsumerConfig等;

应用协议:Protocol、DubboProtocol、HessianProtocol、ThriftProtocol、RmiProtocol、AbstractProxyProtocol、AbstractProtocol等;

注册相关:RegistryProtocol、RegistryFactory、Registry、ZookeeperRegistryFactory、ZookeeperRegistry等

代理和集群相关:Proxy、JdcProxyFactory、AbstractProxyFactory、InvokerInvocationHandler、Cluster、FailoverCluster、FailoverClusterInvoker、AbstractClusterInvoker、Invoker等;

2.时序图

我们通过时序图来分析一下在consumer注册及生产代理对象的过程中,上面的类是如何串联在一起的:

a.spring容器通过DubboBeanDefinitionParser类的对象来解析xml文件中的标签,生成ReferenceConfig等配置对象;

b.ReferenceConfig的init()等方法被调用;

c.通过spi机制确定Protocol接口的实现对象为RegistryProtocol的对象,调用它的refer()方法;

d.通过spi机制确定RegistryFactory接口的实现对象为ZookeeperRegistryFactory,调用它的getRegistry()方法,生产ZookeeperRegistry对象;

e.调用RegistryProtocol对象的doRefer()方法后,并调用FailoverCluster的join()方法,生成FailoverClusterInvoker的对象;

f.调用JdkProxyFactory的getProxy()方法,生成consumer使用接口的代理对象。

3.核心代码

a.ReferenceConfig

    private void init() {
if (initialized) {//判断是否初始化完毕
return;
}
initialized = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
}
// 获取消费者全局配置
checkDefault();//检查
appendProperties(this);
if (getGeneric() == null && getConsumer() != null) {
setGeneric(getConsumer().getGeneric());
}
if (ProtocolUtils.isGeneric(getGeneric())) {
interfaceClass = GenericService.class;
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
}
String resolve = System.getProperty(interfaceName);
String resolveFile = null;
if (resolve == null || resolve.length() == 0) {
resolveFile = System.getProperty("dubbo.resolve.file");
if (resolveFile == null || resolveFile.length() == 0) {
File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
if (userResolveFile.exists()) {
resolveFile = userResolveFile.getAbsolutePath();
}
}
if (resolveFile != null && resolveFile.length() > 0) {
Properties properties = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(resolveFile));
properties.load(fis);
} catch (IOException e) {
throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
} finally {
try {
if(null != fis) fis.close();
} catch (IOException e) {
logger.warn(e.getMessage(), e);
}
}
resolve = properties.getProperty(interfaceName);
}
}
if (resolve != null && resolve.length() > 0) {
url = resolve;
if (logger.isWarnEnabled()) {
if (resolveFile != null && resolveFile.length() > 0) {
logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
} else {
logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
}
}
}
if (consumer != null) {
if (application == null) {
application = consumer.getApplication();
}
if (module == null) {
module = consumer.getModule();
}
if (registries == null) {
registries = consumer.getRegistries();//获取注册信息
}
if (monitor == null) {
monitor = consumer.getMonitor();//获取监控信息
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
checkApplication();
checkStubAndMock(interfaceClass);
Map<String, String> map = new HashMap<String, String>();
Map<Object, Object> attributes = new HashMap<Object, Object>();
map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
if (! isGeneric()) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put("revision", revision);
} String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if(methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put("methods", Constants.ANY_VALUE);
}
else {
map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
map.put(Constants.INTERFACE_KEY, interfaceName);
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, consumer, Constants.DEFAULT_KEY);
appendParameters(map, this);
String prifix = StringUtils.getServiceKey(map);
if (methods != null && methods.size() > 0) {
for (MethodConfig method : methods) {
appendParameters(map, method, method.getName());
String retryKey = method.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(method.getName() + ".retries", "0");
}
}
appendAttributes(attributes, method, prifix + "." + method.getName());
checkAndConvertImplicitConfig(method, map, attributes);
}
}
//attributes通过系统context进行存储.
StaticContext.getSystemContext().putAll(attributes);
ref = createProxy(map);//获取代理对象
} private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用
isJvmRefer = false;
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
//默认情况下如果本地有服务暴露,则引用本地服务.
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
isJvmRefer = isInjvm().booleanValue();
} if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);//获取invoker对象
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // 通过注册中心配置拼装URL
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size() == 0) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
for(URL invo : urls){
System.out.println("invoker's url : "+invo.toFullString());
}
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));//获取invoker对象
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个registry url
}
}
if (registryURL != null) { // 有 注册中心协议的URL
// 对有注册中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
} }
}
System.out.println("real invoker's url :"+invoker.getUrl().toFullString());
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c && ! invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}

dubbo源码分析三:consumer注册及生成代理对象的更多相关文章

  1. Dubbo源码剖析三之服务注册过程分析

    Dubbo源码剖析二之注册中心 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中对注册中心进行了简单的介绍,对Dubbo整合Zookeeper链接源码进行了详细分析.本文接着对服务注册过 ...

  2. dubbo源码分析2-reference bean发起服务方法调用

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  3. dubbo源码分析一:整体分析

    本文作为dubbo源码分析的第一章,先从总体上来分析一下dubbo的代码架构.功能及优缺点,注意,本文只分析说明开源版本提供的代码及功能. 1.dubbo的代码架构:  spring适配层:常规的sp ...

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

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

  5. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  6. Dubbo 源码分析 - 集群容错之 Router

    1. 简介 上一篇文章分析了集群容错的第一部分 -- 服务目录 Directory.服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由.上一篇文章关于服务路由相关逻辑没有 ...

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

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

  8. Dubbo源码分析

    Dubbo源码分析1 Dubbo源码分析2 dubbo源码阅读:rpc请求处理流程(1) 架构设计:系统间通信(17)——服务治理与Dubbo 中篇(分析) 13. Dubbo原理解析-注册中心之Zo ...

  9. dubbo源码分析6-telnet方式的管理实现

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

随机推荐

  1. Javascript的AMD规范

    Javascript发展到今天,已经从一个小丑语言变成了不可替代的前端利器,已经脱离了低端的玩笑脚步,而转变为有规可依的强大语言. 本文主要讲述下如今被大力推广的AMD规范,为什么要AMD,什么场景是 ...

  2. javascript基础学习(七)

    javascript之Object对象 学习要点: 创建Object对象 Object对象属性 Object对象方法 一.创建Object对象 new Object(); new Object(val ...

  3. servlet从xml提取数据登陆

    在xml文档下可以设置参数的初始值,在这里把他当成了数据库在用 <servlet> <description>This is the description of my J2E ...

  4. Direct 2D实现界面库 (2)

    Direct 2D实现界面库 (1) http://www.cnblogs.com/mmc1206x/p/3924580.html 上篇说完了每个 LNode 的绘制过程. 也就是 onDraw 的实 ...

  5. SGU 149. Computer Network

    时间限制:0.25s 空间限制:4M: 题意: 给出一颗n(n<=10000)个节点的树,和n-1条边的长度.求出这棵树每个节点到最远节点的距离: Solution: 对于一个节点,我们可以用D ...

  6. 在CentOS 6.3中安装与配置JDK-7

    在CentOS 6.3中安装与配置JDK-7 来源:互联网 作者:佚名 时间:02-07 16:28:33 [大 中 小] 在CentOS-6.3中安装与配置JDK-7,有需要的朋友可以参考下 安装说 ...

  7. underscorejs-partition学习

    2.25 partition 2.25.1 语法: _.partition(list, predicate, [context]) 2.25.2 说明: 拆分list为两个数组. 第一个数组的元素都满 ...

  8. MySQL Procedure(MySQL存储过程)[转]

    ------Creating Stored Procedures in MySQL------ --Make sure you have version 5 of MySQL:   SELECT VE ...

  9. php如何计算两个时间戳之间相差的日时分秒

    /功能:计算两个时间戳之间相差的日时分秒//$begin_time 开始时间戳//$end_time 结束时间戳function timediff($begin_time,$end_time){ if ...

  10. 马的遍历问题-回溯法应用-ACM

    马的遍历问题 在n*m的棋盘中,马只能走“日” 字.马从位置(x,y)处出发,把棋盘的每一格都走一次,且只走一次.找出所有路径. 问题解的搜索空间? 棋盘的规模是n*m,是指行有n条边,列有m条边. ...