dubbo源码学习
目录
- Dubbo简介
- dubbo SPI
- dubbo provider 提供者
- dubbo consumer 消费者
- dubbo invoker 远程调用
1. dubbo简介
Dubbo是什么?能做什么?
Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。
Dubbo适用于哪些场景?
当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。
当服务越来越多时,服务的URL地址信息就会爆炸式增长,配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。
当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。
接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?等等……
在遇到这些问题时,都可以用Dubbo来解决。
前两段摘自作者梁飞iteye采访
文档基于dubbo版本2.5.3
2. dubbo SPI
ExtensionLoader 实现
getAdaptiveExtension 获取适配类型
- getExtensionLoader 根据类型获取ExtensionLoader实例,如果类型是ExtensionFactory则objectFactory为null,否则为ExtensionFactory的适配类型实现
- getAdaptiveExtension ExtensionLoader获取绑定类型的适配类型实现
- cachedAdaptiveInstance 适配类型实现实例缓存获取,如果缓存为空则创建并缓存
- createAdaptiveExtension 创建适配类型实例
- getAdaptiveExtensionClass 获取适配类型class
- getExtensionClasses 获取扩展类集合
- cachedClasses 扩展类集合缓存获取,不存在则加载并缓存
- loadExtensionClasses 加载扩展类缓存,如果绑定类型SPI注解不为空,并且SPI的value不为空,逗号分隔切分,如果发现大于一个值,抛出异常,不予许有多个默认值,如果存在且为一个value,则作为默认实现的key:cachedDefaultName
- loadFile 加载扩展类,目录顺序:internal/,META-INF/dubbo/,META-INF/services/
- 读取目录下type名称文件,如:com.alibaba.dubbo.common.extension.ExtensionFactory
- 读取文件内容的实现,如果class存在Adaptive注解,缓存至cachedAdaptiveClass
- 如果class不存在Adaptive注解,但是存在以绑定类型为入参的构造器函数,则缓存至cachedWrapperClasses。(例如:public MyFilterWrapper(com.alibaba.dubbo.rpc.Filter filter))
- 如果不是包装类型,如果存在"="分隔的kv,则以key为name属性,否则以class实现的类名称去除绑定类型名称的剩余部分的小写作为name属性,如:AdaptiveExtensionFactory去除ExtensionFactory,name为:adaptive
- 逗号分隔name属性如果不为空,如果实现类存在Activate注解,将Activate注解以names[0]为key缓存至cachedActivates;遍历names,将name以class为key缓存至cachedNames,将class以name为key放入extensionClasses返回,即缓存至:cachedClasses
- 如果cachedAdaptiveClass不为空返回,否则使用javassist生成适配类型class,创建适配class完成,调用无参构造器实例化
- 生成class适配类规则
- 走到自动生成模块说明不存在Adaptive注解的实现类,遍历绑定接口的方法
- 如果方法不存在adaptive注解,则抛出异常
- 如果存在,则生成代码逻辑,如果Adaptive不存在values,即没有设置Key,则使用接口类名称驼峰转点分隔的方式作为Key,例如:ProtocolTest转换后为protocol.test
- 如果Adaptive注解存在values,遍历,如果value是"protocol",则使用url.getProtocol作为extName(后面自动生成的代码中会根据extName获取扩展实现类:getExtension(extName));如果不是protocol,如果参数存在Invocation类型,则使用url.getMethodParameter(methodName,value,defaultName)方式获取值作为extName,method,否则使用url.getParameter(value,defaultName)作为extName
- 生成方法实现,使用ExtensionLoader.getExtension(extName)获取扩展实现,作为接口方法的委托实现
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException(
"method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException(
"method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter export(
com.alibaba.dubbo.rpc.Invoker arg0)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException(
"com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ((url.getProtocol() == null) ? "dubbo"
: url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
com.alibaba.dubbo.common.URL arg1)
throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) {
throw new IllegalArgumentException("url == null");
}
com.alibaba.dubbo.common.URL url = arg1;
String extName = ((url.getProtocol() == null) ? "dubbo"
: url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.refer(arg0, arg1);
}
}
- injectExtension 注入扩展类,如果类中存在扩展类型属性则注入,objectFactory.getExtension工厂获取注入类型实例,根据set方法参数类型及set方法截取set之后剩余的部分取小写,如:StubProxyFactoryWrapper.setProtocol(Protocol protocol)
getExtension 获取扩展类型
- 如果名称是true,则getDefaultExtension获取默认实现类,即:SPI注解的value属性
- cachedInstances 从缓存中获取实例的holder,如果不存在则创建并绑定holder(绑定的为完成注入及wrapper包装的实例)
- createExtension 从cachedClasses中根据name获取扩展类,如果为空抛出异常,从EXTENSION_INSTANCES(缓存的为 未注入及wrapper包装 的实例)缓存实例中获取name对应的实例,如果为空,则根据class创建并缓存
- injectExtension 为实例注入扩展实现
- cachedWrapperClasses 遍历wrapper类,包装实例并为wrapper类 injectExtension 注入扩展实现
injectExtension 注入扩展属性
- 如果类中存在扩展类型属性则注入,objectFactory.getExtension工厂获取注入类型实例,根据set方法参数类型及set方法截取set之后剩余的部分取小写,如:StubProxyFactoryWrapper.setProtocol(Protocol protocol)
- objectFactory.getExtension 工厂创建扩展实例,即遍历SpiExtensionFactory,SpringExtensionFactory两个工厂创建实例,SpiExtensionFactory根据类型创建适配类型实例,SpringExtensionFactory根据name从Spring上下文中获取实例(即set方法名截取set后的部分小写)
getActivateExtension 获取 Activate 注解扩展实现
public List<T> getActivateExtension(URL url, String key) {
return getActivateExtension(url, key, null);
}
public List<T> getActivateExtension(URL url, String[] values) {
return getActivateExtension(url, values, null);
}
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
- 根据入参的URL,values(根据指定的key获取values,如:default.service.filter=-exception,businessException),group获取扩展实现,values作为names集合
- 如果names不包含"-default",则遍历cachedActivates注解缓存,如果group与Activate注解的group匹配,则按照name获取扩展实现,如果names不包含cachedActivates的key(即:注解的value属性的值,对应spi的name=value),并且names不包含"-name",并且 Activate注解value属性为空 或者 URL的parameters中包含Activate注解的value属性中的一个配置不为空,则将扩展添加至exts集合
- 遍历names集合,如果name不是"-“开头,并且names不包含”-name",如果name是"default",则usrs扩展实现列表添加至exts扩展实现列表的头部并清空usrs(避免后面usrs往exts中添加时重复添加),否则根据name获取扩展实现添加至usrs;如果usrs不为空,则添加至exts中,返回扩展实现列表exts
3. 服务提供者 dubbo provider
ServiceConfig.export 导出暴露服务
- 如果providerConfig不为空,则判断provider是否已经暴露过,是则返回,如果delay为空,获取配置中的delay,启动一个线程(DelayExportServiceThread)使用Thread.sleep延迟调用doExport
ServiceConfig.doExport step1
- interfaceName接口必须配置不能为空:"<dubbo:service interface="" /> interface not allow null!"
- checkDefault:如果providerConfig为空新建,并且填入默认参数appendProperties
- appendProperties开始
- 注入方法使用配置属性的set方法,并且必须是public,入参个数为1并且是原始类型
- 从System中获取配置赋值给本地value,参数命名规则:dubbo.类名称去除Config,Bean后小写,例如:ProviderConfig=provider.[如果有配置id则增加id].方法名称去除set并小写
- 如果本地value依然为空,则尝试从实例中调用get或is方法获取属性判断属性是否获取的值为空,如果是,则再次尝试从System中按照前一步那样获取配置属性,如果为空则尝试读取:dubbo.properties.file配置指定的配置文件中的配置,如果没有配置则使用默认配置文件:dubbo.properties
- 如果本地value依然为空,判断当前属性是否属于legacyProperties集合中,如果是将其对应的key按照前两步步骤获取并赋值给本地value
- appendProperties完成
- 调用set方法将value赋值
ServiceConfig.doExport step2
- ProviderConfig如果不为空,从ProviderConfig中为application,module,registries,monitor,protocols赋值
- ModuleConfig如果不为空,从ModuleConfig中为registries,monitor赋值
- ApplicationConfig如果不为空,从ApplicationConfig中为registries,monitor赋值
- 如果ref实现类是GenericService类型,则标识generic=true,interfaceClass = GenericService.class(通用服务接口),否则反射获取类型为interfaceClass赋值,校验接口与方法:checkInterfaceAndMethods,校验实现:checkRef,标识generic=false
- local如果不为空并且为true,则覆写为interfaceName+“Local”,校验interfaceClass是否为local接口的实现
- stub如果不为空并且为true,则覆写为interfaceName+“Stub”,校验interfaceClass是否为stub接口的实现
- checkApplication同checkDefault
- checkRegistry
- checkProtocol
- appendProperties(this) 为当前实例赋值
- checkStubAndMock
- 如果path为空则为其赋值:interfaceName;
- doExportUrls导出Urls
doExportUrls 将提供者导出为URL路径
- loadRegistries从注册中心加载URL路径列表,url均设置为registry协议
- RegistryConfig获取address,如果为空则使用0.0.0.0
- 从System中获取dubbo.registry.address配置,如果存在则覆写address
- appendParameters从ApplicationConfig收集属性至map中
- appendParameters开始
- 如果是get,is方法,不包含getClass方法,public修饰,无参方法,返回类型为原始类型
- 获取Parameter注解,如果为excluded表示不需要收集该属性至map集合
- parameters map的key使用Parameter注解的key属性,如果不存在则使用方法名称小写,根据key从parameters
- 如果Parameter注解为escape则调用URLEncode.encode编译,如果Parameter注解为append,则从parameters map中获取之前的属性,如果存在使用逗号拼接,如果存在prefix,则添加前缀
- 将处理后的value属性放入parameters map 使用 key
- 如果方法名为getParameters,public修饰,无参方法,返回map,调用方法返回map,遍历map,将之前存在的key与新map的key使用"."拼接,value使用新map的value
- appendParameters完成
- appendParameters从ServiceConfig收集属性至map中
- map放入path,dubbo,时间戳,pid属性
map.put("path", RegistryService.class.getName());
map.put("dubbo", Version.getVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
- 如果此时map已然不包含"protocol"属性,则根据RegistryFactory的实现类配置,如果存在remote的key对应的实现,则使用remote,否则使用dubbo
dubbo=com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory
multicast=com.alibaba.dubbo.registry.multicast.MulticastRegistryFactory
zookeeper=com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
redis=com.alibaba.dubbo.registry.redis.RedisRegistryFactory
- parseURLs根据address,map解析组装URL集合
- parseURL开始
- 如果address包含"😕/"字符,则直接赋值给url
- 如果address不包含"😕/"字符,则使用逗号切分,第一个作为url,其余的如果存在则作为backup参数
- 从map集合中获取protocol协议,不存在则使用dubbo协议
- 根据url字符串获取URL对象,如果解析的URL对象的协议为空则使用default集合中的默认配置,用户名,密码,端口,path路径,参数集合同样如果为空使用默认值
- parseURL结束
- 遍历URLs集合,为每个URL的registry参数设置为url.getProtocol(),URL设置为registry协议,如果是provider并且register参数为true,或者,不是provider并且subscribe为true,则将url添加至注册URL列表中
- ProtocolConfig配置列表遍历导出URL:doExportUrlsFor1Protocol(protocolConfig, registryURLs);
doExportUrlsFor1Protocol 为单个协议导出provider服务URL
- ProtocolConfig协议名称没配置使用dubbo协议
- ProtocolConfig主机host没配置则使用ProviderConfig的host
- 如果host为无效的(localhost,0.0.0.0等),anyhost置为true,则创建socket与注册中心连接一次,通过socket获取本地地址。如果依然无效,使用工具包获取本地地址
- ProtocolConfig端口port没配置则使用ProviderConfig的port
- 如果端口依然为空或者0,则使用默认端口:根据协议name获取协议的实现类中的默认端口号,如:DubboProtocol默认端口:20880
- 如果端口依然为空,则随机获取当前本机可使用的端口号
- 创建map准备收集配置参数
- 如果anyhost=true则添加anyhost为true至map
- map中添加side,dubbo,时间戳,pid
- 收集各个配置属性
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, provider, Constants.DEFAULT_KEY);
appendParameters(map, protocolConfig);
appendParameters(map, this);
- map中添加方法属性,token令牌属性
- 导出服务
- 根据协议名称,主机,端口,路径,以及map参数列表创建URL
- 根据协议获取ConfiguratorFactory接口实现,如果有,则调用实现类configure方法处理url
override=com.alibaba.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
absent=com.alibaba.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
- 如果scope配置为none则为不暴露,local,remote,该属性可在ServiceConfig中指定,默认为null,即暴露本地也暴露远程
- 配置为none不暴露
- 配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
- 如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
ServiceConfig.exportLocal 本地暴露服务
- 仅将服务暴露在本地jvm中
ServiceConfig.export 远程暴露服务
- 如果注册中心URL列表不为空,并且服务URL的register参数为true(没配置则默认为true),遍历注册中心注册暴露服务
- proxyFactory.getInvoker代理工厂获取代理对象Invoker,代理工厂为适配模式,即根据url协议动态调用相对应的实现方法,默认的实现为StubProxyFactoryWrapper包装的JavassistProxyFactory,Invoker对应AbstractProxyInvoker的实现使用javassist字节码生成代理对象Wrapper
url:registry://192.168.11.29:2181/com.alibaba.dubbo.registry.RegistryService?application=dispatch-inverse-monitor&backup=192.168.11.32:2181,192.168.11.20:2181&dubbo=2.5.3&export=dubbo://192.168.103.186:15030/com.dianwoba.pt.goodjob.remote.service.JobExecuteService?accesslog=true&anyhost=true&application=dispatch-inverse-monitor&dubbo=2.5.3&group=dispatch-inverse-monitor_analyzeService&interface=com.dianwoba.pt.goodjob.remote.service.JobExecuteService&logger=slf4j&methods=execute&pid=9892&retries=0&revision=1.1.0-SNAPSHOT&side=provider&timeout=5000×tamp=1544673080385&logger=slf4j&pid=9892®istry=zookeeper×tamp=1544673080385
- protocol.export 协议同样为适配模式,根据url获取相应的协议实现进行导出暴露服务,RegistryProtocol.export导出
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
...
public com.alibaba.dubbo.rpc.Exporter export(
com.alibaba.dubbo.rpc.Invoker arg0)
throws com.alibaba.dubbo.rpc.RpcException {
...
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ((url.getProtocol() == null) ? "dubbo"
: url.getProtocol());
...
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
com.alibaba.dubbo.common.URL arg1)
throws com.alibaba.dubbo.rpc.RpcException {
...
com.alibaba.dubbo.common.URL url = arg1;
String extName = ((url.getProtocol() == null) ? "dubbo"
: url.getProtocol());
...
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
graph LR
ProtocolListenerWrapper-->ProtocolFilterWrapper
ProtocolFilterWrapper-->RegistryProtocol
RegistryProtocol.export Registry协议导出服务 step1
- RegistryProtocol.doLocalExport 导出invoker
- doLocalExport 开始
- 从bouds缓存中获取,如果没有则导出并缓存
- 将originInvoker封装为InvokerDelegete委派对象,并将export服务url与委派对象绑定
- protocol.export 导出服务,此时的协议是用ExtensionLoader注入的适配类型,导出的invoker绑定的url是ProtocolConfig协议,如:DubboProtocol,导出后的包装链
graph LR
ExporterChangeableWrapper-->ListenerExporterWrapper
ListenerExporterWrapper-->DubboExporter
DubboExporter-->ProtocolFilterWrapper.Invoker
ProtocolFilterWrapper.Invoker-->InvokerDelegete
InvokerDelegete-->DubboInvoker
- doLocalExport 完成
- openServer服务端打开服务端口
- url参数判断isserver参数是否为true,默认为true
- serverMap缓存获取server,如果不存在创建服务
RegistryProtocol.export Registry协议导出服务 step2 createServer创建服务
- 默认开启server关闭时发送readonly事件
- 默认开启heartbeat,60秒
- 获取url参数server,默认为netty,ExtensionLoader判断该类型Transporter是否支持,不支持抛出异常
- Exchangers.bind(url, requestHandler)绑定端口提供服务
- Exchanger默认为HeaderExchanger(目前仅支持该类型)
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
- 执行Transporters绑定com.alibaba.dubbo.remoting.Transporters#bind(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.remoting.ChannelHandler…),如果是多个handler则将多个handler封装为ChannelHandlerDispatcher进行绑定
- HeaderExchangeServer构造器中启动心跳,如果url中没有配置,缺省为0,即不启动心跳
- com.alibaba.dubbo.remoting.Transporters#getTransporter获取Transporter执行.bind(url, handler),NettyTransporter绑定url与handler
public Server bind(URL url, ChannelHandler listener) throws RemotingException {
return new NettyServer(url, listener);
}
- NettyServer构造器包装handler,并在url中指定线程池名称:DubboServerHandler
public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
- HeartbeatHandler封装的ChannelHandler句柄通过com.alibaba.dubbo.remoting.Dispatcher#dispatch返回。例如:AllChannelHandler
- AllChannelHandler父类WrappedChannelHandler构造器中ExtensionLoader.getAdaptiveExtension初始化线程池,dataStore缓存线程池,缓存key使用(ExecutorService.class.getName()或者consumer:如果url中side为consumer的情况时),默认为fix线程池,默认配置:线程名称为Dubbo,大小200,队列0,拒绝策略:AbortPolicyWithReport
graph LR
MultiMessageHandler-->HeartbeatHandler
HeartbeatHandler-->AllChannelHandler
AllChannelHandler-->DecodeHandler
DecodeHandler-->HeaderExchangeHandler
HeaderExchangeHandler-->DubboProtocol.ExchangeHandlerAdapter
- handler链初始化完成,调用父类构造器:AbstractServer
- AbstractServer构造器中打开服务doOpen
- 调用子类NettyServer.doOpen打开服务,并将NettyServer与NettyHandler绑定,响应客户端请求
RegistryProtocol.export Registry协议导出服务 step3
- RegistryProtocol.getRegistry 根据原始invoker的URL获取注册中心,如:zk协议的ZookeeperRegistryFactory,创建zk注册中心:new ZookeeperRegistry(url, zookeeperTransporter);
- 初始化FailbackRegistry,默认5秒检查一次与注册中心的连接
- 初始化AbstractRegistry,notify通知注册中心的监听器
- 继续构造ZookeeperRegistry
- 缺省使用zkclinet连接zk注册中心,ZkclientZookeeperClient,添加zk状态监听器StateListener
- RegistryProtocol.getRegistedProviderUrl返回注册到注册中心的URL,对URL参数进行一次过滤
- 将dubbo服务url注册至注册中心,创建zk数据节点,如dubbo协议:dubbo://…
- getSubscribedOverrideUrl将协议设置为provider
- 创建provider url监听器OverrideListener
- registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);订阅注册中心的provider url
- 返回export
完成导出并将export缓存至ServiceConfig对象的exporters
4. 服务消费者 dubbo consumer
ReferenceConfig.get 获取服务引用
- 如果ref引用为空,则初始化init
ReferenceConfig.init 初始化
- checkDefault 如果ConsumerConfig配置为空,则创建并使用缺省配置
- appendProperties 使用系统配置填充ReferenceConfig
- 类似提供者,如果ConsumerConfig不为空,application、module、registries、monitor为空使用ConsumerConfig获取;其次使用module、application获取兜底配置
- 类似提供者,checkApplication,checkStubAndMock,组装默认配置集合map,其中attributes通过系统context进行存储(MethodConfig的属性),同样使用appendAttributes方法收集至attributes集合中
- 使用map默认配置创建代理对象
ReferenceConfig.createProxy 使用map默认配置创建代理对象
- 如果是injvm协议,则按照Injvm协议获取引用
- 如果ReferenceConfig配置了url则使用点对点方式拼接URL列表,url配置使用分号分隔
- 如果是注册中心方式,从注册中心拼装URL列表,类似提供者调用loadRegistries组装注册中心URL列表,URL协议均设置为registry
- 如果注册中心地址只有一个,则调用refprotocol.refer获取invoker
invoker = refprotocol.refer(interfaceClass, urls.get(0));
url:
registry://192.168.11.29:2181/com.alibaba.dubbo.registry.RegistryService?application=dispatch-inverse-monitor&backup=192.168.11.32:2181,192.168.11.20:2181&dubbo=2.5.3&logger=slf4j&pid=1908&refer=application=dispatch-inverse-monitor&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,acceptOrderRateAndOvertimeRateByCityId,queryGeoHashMonthSale&pid=1908&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544767680215®istry=zookeeper×tamp=1544767680267
- 如果注册中心地址存在多个,调用refprotocol.refer获取所有注册中心的invoker,并调用AvailableCluster的cluster.join负载均衡的方式选择一个invoker
- refprotocol适配类型,调用链
- 创建invoker完成后判断是否需要校验check,xml配置中对应check属性,默认check=true,通常配置check=false(即不校验)。如果开启了校验则会判断提供者是否存在(所有连接中存在一个已连接并且不为只读状态)
graph LR
ProtocolListenerWrapper-->ProtocolFilterWrapper.Invoker
ProtocolFilterWrapper.Invoker-->RegistryProtocol
RegistryProtocol.refer 获取服务invoker引用
- 根据注册中心url获取Registry实例,adaptive指定协议为key,即获取的是协议对应的注册中心实例,如:ZookeeperRegistryFactory.getRegistry返回ZookeeperRegistry,过程同提供者相同
- 如果服务是RegistryService类型,则proxyFactory.getInvoker((T) registry, type, url)获取invoker
- 如果refer参数对应的group属性存在,使用MergeableCluster获取invoker
- 缺省调用Cluster适配类型获取invoker
拼装后的url:zookeeper://192.168.11.29:2181/com.alibaba.dubbo.registry.RegistryService?application=dispatch-inverse-monitor&backup=192.168.11.32:2181,192.168.11.20:2181&dubbo=2.5.3&logger=slf4j&pid=16340&refer=application=dispatch-inverse-monitor&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,acceptOrderRateAndOvertimeRateByCityId,queryGeoHashMonthSale&pid=16340&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544702578695×tamp=1544702578729
RegistryProtocol.doRefer 获取服务invoker引用 step1
- 创建注册中心目录对象RegistryDirectory
- RegistryDirectory设置注册中心Registry、协议Protocol(适配类型)
- 创建消费者URL
consumer://192.168.103.186/com.dianwoba.dw.schedule.service.ByTheWayDriftService?application=dispatch-inverse-monitor&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,queryGeoHashMonthSale,acceptOrderRateAndOvertimeRateByCityId&pid=15244&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544703002095
- 如果注册中心URL配置interface不是"*"、register参数没配置或者为true,则将消费者URL注册至注册中心,创建zk临时数据节点,category为consumers
目录:
/dubbo/com.dianwoba.dw.schedule.service.ByTheWayDriftService/consumers/consumer://192.168.103.186/com.dianwoba.dw.schedule.service.ByTheWayDriftService?application=dispatch-inverse-monitor&category=consumers&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,queryGeoHashMonthSale,acceptOrderRateAndOvertimeRateByCityId&pid=2872&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544703323067
- RegistryDirectory.subscribe 注册中心目录订阅,入参URL的category参数改为providers、routers、configurations目录即订阅改三个目录
- RegistryDirectory.subscribe->Registry.subscribe->ZookeeperRegistry.subscribe 使用传入的ZKRegistry订阅,回调父类AbstractRegistry.subscribe,RegistryDirectory作为监听器以URL为key放入subscribed中
url:
consumer://192.168.103.186/com.dianwoba.dw.schedule.service.ByTheWayDriftService?application=dispatch-inverse-monitor&category=providers,configurators,routers&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,queryGeoHashMonthSale,acceptOrderRateAndOvertimeRateByCityId&pid=13596&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544703743811
- 如果消费URL的接口interface参数为ANY_VALUE(即*),则订阅root(即/dubbo)目录下的所有子目录服务变更的消息
- FailbackRegistry回调父类结束调用钩子方法doSubscribe
- 如果url的interface属性为"*",zk创建root节点,并添加监听
- 如果url的interface属性不为"*",获取url目录属性(providers,configurators,routers),zk创建节点并监听
- 如果监听目录非空(即存在提供者)
- 如果没有提供者则使用empty协议,消费者与提供者匹配上,host使用消费者的host,side=consumer,放入urls
- 如果有提供者,消费者与提供者匹配上,放入urls
- notify(url:消费者, listener, urls:提供者) 通知
zk创建节点目录:path属性
提供者目录:/dubbo/com.dianwoba.dw.schedule.service.ByTheWayDriftService/providers
配置目录:/dubbo/com.dianwoba.dw.schedule.service.ByTheWayDriftService/configurators
路由目录:/dubbo/com.dianwoba.dw.schedule.service.ByTheWayDriftService/routers
返回目录下子文件,提供者URL:children属性
urls:
empty://192.168.103.186/com.dianwoba.dw.schedule.service.ByTheWayDriftService?application=dispatch-inverse-monitor&category=providers&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,acceptOrderRateAndOvertimeRateByCityId,queryGeoHashMonthSale&pid=15120&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544705297452
empty://192.168.103.186/com.dianwoba.dw.schedule.service.ByTheWayDriftService?application=dispatch-inverse-monitor&category=configurators&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,acceptOrderRateAndOvertimeRateByCityId,queryGeoHashMonthSale&pid=15120&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544705297452
empty://192.168.103.186/com.dianwoba.dw.schedule.service.ByTheWayDriftService?application=dispatch-inverse-monitor&category=routers&check=false&default.check=false&default.retries=0&default.timeout=1000&dubbo=2.5.3&interface=com.dianwoba.dw.schedule.service.ByTheWayDriftService&logger=slf4j&methods=acceptOrderRateAndOvertimeRateListByCityIdList,acceptOrderRateAndOvertimeRateByCityId,queryGeoHashMonthSale&pid=15120&revision=1.0.0-20181204.071850-923&side=consumer&timeout=100×tamp=1544705297452
dubbo://10.201.1.195:13540/com.dianwoba.rider.contract.provider.RiderContractProvider?accesslog=false&anyhost=true&application=rider-contract-unit-service&default.accesslog=false&default.retries=0&default.service.filter=-exception,asyncTransmitFilter,responseFilter&default.threadpool=monitorPool&dispatcher=message&dubbo=2.0.1&findByTime.timeout=500&generic=false&interface=com.dianwoba.rider.contract.provider.RiderContractProvider&logger=slf4j&methods=findByDateRange,findAllCainiaoByTime,findAllExpressByTime,findAllNonExpressRiders,findCityAllContractRidersByTime,findByTime,findAllByTime,findWithoutRecruitByDateRange&pid=1&side=provider&threadpool=monitorPool&threads=1000&timeout=2000×tamp=1544508939139
消费者URL与提供者URL匹配:
return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup))
&& (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion))
&& (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier));
- notify通知,回调父类notify,回调父类doNotify,如果失败,将失败的通知请求记录到失败列表,定时重试,回调父类notify,保存url配置并通知监听者RegistryDirectory.notify(包含重写的url:overrideDirectoryUrl,如果客户端没有配置timeout超时会继承提供者的timeout超时配置)
- RegistryDirectory.refreshInvoker刷新invoker
- 如果是empty协议,清空本地该服务的所有invoker引用
- 如果invokerUrls不为空(即存在提供者),toInvokers封装为Invoker缓存至urlInvokerMap,toInvokers返回Invoker包装
RegistryProtocol.doRefer toInvokers创建Invoker step2
- 如果缓存中不存在Invoker,则重新引用
按照提供者URL封装Invoker
invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
graph LR
InvokerDelegete-->ListenerInvokerWrapper
ListenerInvokerWrapper-->ProtocolFilterWrapper
ProtocolFilterWrapper-->DubboInvoker
- 根据url协议获取invoker引用:DubboProtocol.refer
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
- getClients获取clients集合,如果参数connections没配置,默认设为0,代表共享连接,默认为共享连接。否则按照指定connections数量初始化多个连接
- getSharedClient缓存中获取,以ip:port为key缓存,获取如果不存在,则新建:initClient
- initClient 新建连接
- initClient 新建连接,根据lazy参数决定是否使用懒惰连接
- LazyConnectExchangeClient客户端连接,在实际调用的时候判断如果没有连接再创建。默认使用HeaderExchangeClient
- Exchangers.connect直接连接获取客户端,根据url的exchanger参数获取Exchange进行连接
- 目前只有一种实现HeaderExchanger
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
- Transporters.connect连接,ExchangeHandlerAdapter增加两层包装:DecodeHandler、HeaderExchangeHandler
graph LR
DecodeHandler-->HeaderExchangeHandler
HeaderExchangeHandler-->ExchangeHandlerAdapter
- 获取Transporter适配类型调用connect,默认走netty
- NettyTransporter.connect,返回NettyClient(url, listener)
public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException{
super(url, wrapChannelHandler(url, handler));
}
- wrapChannelHandler继续包装监听handle
graph LR
MultiMessageHandler-->HeartbeatHandler
HeartbeatHandler-->AllChannelHandler
AllChannelHandler-->DecodeHandler
- 父类AbstractClient构造器中打开连接doOpen
- NettyClient.doOpen,将包装后的handle再次包装为NettyHandler,NettyHandle通过NettyClient回调handle监听
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
bootstrap = new ClientBootstrap(channelFactory);
// config
// @see org.jboss.netty.channel.socket.SocketChannelConfig
bootstrap.setOption("keepAlive", true);
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("connectTimeoutMillis", getTimeout());
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
}
- 创建client完成,client注入DubboInvoker,创建Invoker完成
- Directory订阅完成
RegistryProtocol.doRefer 包装invoker引用 step3
- cluster.join(directory) 根据目录组装Invoker
graph LR
MockClusterWrapper-->FailoverCluster
- 再次包装Directory中包装的InvokerDelegete,FailoverClusterInvoker按照重试次数循环调用invoker
graph LR
MockClusterInvoker-->FailoverClusterInvoker
FailoverClusterInvoker-->InvokerDelegete
proxyFactory.getProxy(invoker) 创建服务代理
- proxy参数没有配置情况下,缺省StubProxyFactoryWrapper包装的JavassistProxyFactory创建代理
- StubProxyFactoryWrapper.getProxy,调用包装类的getProxy即适配类型的代理工厂,默认JavassistProxyFactory创建InvokerInvocationHandler得代理
- 创建完成返回代理对象
5. Invoker客户端调用服务端
1. MockClusterInvoker调用
- 如果方法是mock方法,生成mock方法,直接返回mock的返回值,不是则继续调用绑定的invoker
2. FailoverClusterInvoker 调用
- list列举所有invokers
- 负载均衡获取invoker,默认rundom随机
- 按照负载均衡算法获取invoker调用
- 如果失败了根据重试次数重试(循环的方式)
3. InvokerDelegete 调用
- 直接调用绑定的invoker
4. ListenerInvokerWrapper
- 构造器中将invoker回调Listener.referred通知监听器
- invoker直接调用绑定的invoker
ProtocolFilterWrapper.Invoker 内部类调用
- 通过Filter链调用下层Invoker(DubboInvoker),filter.invoke(next, invocation);
DubboInvoker调用
- invoker调用子类doInvoker方法,调用是顺序遍历的方式选择客户端,客户端缓存是按照ip:port为key
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
- 如果是oneWay则只负责发送后直接返回一个空的RpcResult,调用currentClient.send
- 如果是Async,调用currentClient.request,同样发送后返回一个空的RpcResult,将异步调用的ResponseFuture与回调动作FutureAdapter绑定到RpcContext的ThreadLocal
- 如果是同步的则同步等待RpcResult,调用currentClient.request
currentClient.request 客户端发送调用请求
按照非懒惰方式客户端梳理
- HeaderExchangeClient.request 发送请求
- 委派HeaderExchangeChannel.request 发送send请求,如果消息是Request,Response,String类型之一,直接发送,否则封装为Request类型发送
- HeaderExchangeChannel.channel(NettyClient)链调用send,HeaderExchangeChannel.request请求前创建DefaultFuture实例监控超时,DefaultFeature.RemotingInvocationTimeoutScan扫描超时服务,DubboResponseTimeoutScanTimer线程每30毫秒扫描一次
- NettyClient.send 发送消息
- getChannel获取通道发送消息
- NettyChannel.getOrAddChannel从缓存中获取与netty Channel绑定的dubbo的NettyChannel
protected com.alibaba.dubbo.remoting.Channel getChannel() {
Channel c = channel;
if (c == null || ! c.isConnected())
return null;
return NettyChannel.getOrAddChannel(c, getUrl(), this);
}
- NettyChannel发送消息
ChannelFuture future = channel.write(message);
if (sent) {
timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
success = future.await(timeout);
}
Throwable cause = future.getCause();
6. 服务端接受客户端请求并响应
假定以netty协议为通信进行梳理
- NettyHandler服务端接收到客户端消息,调用handler链
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
try {
handler.received(channel, e.getMessage());
} finally {
NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
}
}
graph LR
MultiMessageHandler-->HeartbeatHandler
HeartbeatHandler-->AllChannelHandler
AllChannelHandler-->DecodeHandler
DecodeHandler-->HeaderExchangeHandler
HeaderExchangeHandler-->DubboProtocol.ExchangeHandlerAdapter
- MultiMessageHandler:如果消息是MultiMessage类型,则遍历调取后续的handler链,否则直接调用下一节点
- HeartbeatHandler:添加时间戳属性READ_TIMESTAMP,如果消息是Request并且是心跳Request,则判断是否twoWay模式,如果是,则封装Response发送心跳事件Response.HEARTBEAT_EVENT,如果消息是心跳的Response则打印日志,继续调用下一节点
- AllChannelHandler:将消息接收任务封装为ChannelEventRunnable线程放入线程池中处理,线程池执行直接调用下一节点
- DecodeHandler:如果消息是Decodeable类型,则反编译消息,继续调用下一节点
- HeaderExchangeHandler
- 如果是Request类型,如果是事件消息,则handle事件(判断是否READONLY事件,如果是,为Channel设置READONLY,否则,如果是twoway,判断请求data是否为空或者异常,是则直接响应Response,调用下一节点并响应Response;如果不是twoway直接调用下一节点
- 如果是Response类型,如果是心跳,则处理心跳消息
- 如果是String类型,则调用下一节点的telnet方法处理
- 兜底直接调用下一节点received
- DubboProtocol.ExchangeHandlerAdapter:调用invoker代理返回Result结果
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Invocation) {
reply((ExchangeChannel) channel, message);
} else {
super.received(channel, message);
}
}
- HeaderExchangeHandler如果是twoway,将DubboProtocol.ExchangeHandlerAdapter返回的Result封装为Response响应回客户端