dubbo源码学习

dubbo源码学习

目录

  1. Dubbo简介
  2. dubbo SPI
  3. dubbo provider 提供者
  4. dubbo consumer 消费者
  5. dubbo invoker 远程调用

1. dubbo简介

Dubbo是什么?能做什么?

Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。

Dubbo适用于哪些场景?

当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。

当服务越来越多时,服务的URL地址信息就会爆炸式增长,配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。

当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。

接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?等等……

在遇到这些问题时,都可以用Dubbo来解决。

前两段摘自作者梁飞iteye采访

文档基于dubbo版本2.5.3

2. dubbo SPI

ExtensionLoader 实现
getAdaptiveExtension 获取适配类型
  1. getExtensionLoader 根据类型获取ExtensionLoader实例,如果类型是ExtensionFactory则objectFactory为null,否则为ExtensionFactory的适配类型实现
  2. getAdaptiveExtension ExtensionLoader获取绑定类型的适配类型实现
  3. cachedAdaptiveInstance 适配类型实现实例缓存获取,如果缓存为空则创建并缓存
  4. createAdaptiveExtension 创建适配类型实例
  5. getAdaptiveExtensionClass 获取适配类型class
  6. getExtensionClasses 获取扩展类集合
  7. cachedClasses 扩展类集合缓存获取,不存在则加载并缓存
  8. loadExtensionClasses 加载扩展类缓存,如果绑定类型SPI注解不为空,并且SPI的value不为空,逗号分隔切分,如果发现大于一个值,抛出异常,不予许有多个默认值,如果存在且为一个value,则作为默认实现的key:cachedDefaultName
  9. loadFile 加载扩展类,目录顺序:internal/,META-INF/dubbo/,META-INF/services/
  1. 读取目录下type名称文件,如:com.alibaba.dubbo.common.extension.ExtensionFactory
  2. 读取文件内容的实现,如果class存在Adaptive注解,缓存至cachedAdaptiveClass
  3. 如果class不存在Adaptive注解,但是存在以绑定类型为入参的构造器函数,则缓存至cachedWrapperClasses。(例如:public MyFilterWrapper(com.alibaba.dubbo.rpc.Filter filter))
  4. 如果不是包装类型,如果存在"="分隔的kv,则以key为name属性,否则以class实现的类名称去除绑定类型名称的剩余部分的小写作为name属性,如:AdaptiveExtensionFactory去除ExtensionFactory,name为:adaptive
  5. 逗号分隔name属性如果不为空,如果实现类存在Activate注解,将Activate注解以names[0]为key缓存至cachedActivates;遍历names,将name以class为key缓存至cachedNames,将class以name为key放入extensionClasses返回,即缓存至:cachedClasses
  1. 如果cachedAdaptiveClass不为空返回,否则使用javassist生成适配类型class,创建适配class完成,调用无参构造器实例化
  1. 生成class适配类规则
  2. 走到自动生成模块说明不存在Adaptive注解的实现类,遍历绑定接口的方法
  3. 如果方法不存在adaptive注解,则抛出异常
  4. 如果存在,则生成代码逻辑,如果Adaptive不存在values,即没有设置Key,则使用接口类名称驼峰转点分隔的方式作为Key,例如:ProtocolTest转换后为protocol.test
  5. 如果Adaptive注解存在values,遍历,如果value是"protocol",则使用url.getProtocol作为extName(后面自动生成的代码中会根据extName获取扩展实现类:getExtension(extName));如果不是protocol,如果参数存在Invocation类型,则使用url.getMethodParameter(methodName,value,defaultName)方式获取值作为extName,method,否则使用url.getParameter(value,defaultName)作为extName
  6. 生成方法实现,使用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);
    }
}
  1. injectExtension 注入扩展类,如果类中存在扩展类型属性则注入,objectFactory.getExtension工厂获取注入类型实例,根据set方法参数类型及set方法截取set之后剩余的部分取小写,如:StubProxyFactoryWrapper.setProtocol(Protocol protocol)
getExtension 获取扩展类型
  1. 如果名称是true,则getDefaultExtension获取默认实现类,即:SPI注解的value属性
  2. cachedInstances 从缓存中获取实例的holder,如果不存在则创建并绑定holder(绑定的为完成注入及wrapper包装的实例
  3. createExtension 从cachedClasses中根据name获取扩展类,如果为空抛出异常,从EXTENSION_INSTANCES(缓存的为 未注入及wrapper包装 的实例)缓存实例中获取name对应的实例,如果为空,则根据class创建并缓存
  4. injectExtension 为实例注入扩展实现
  5. cachedWrapperClasses 遍历wrapper类,包装实例并为wrapper类 injectExtension 注入扩展实现
injectExtension 注入扩展属性
  1. 如果类中存在扩展类型属性则注入,objectFactory.getExtension工厂获取注入类型实例,根据set方法参数类型及set方法截取set之后剩余的部分取小写,如:StubProxyFactoryWrapper.setProtocol(Protocol protocol)
  2. 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);
}
  1. 根据入参的URL,values(根据指定的key获取values,如:default.service.filter=-exception,businessException),group获取扩展实现,values作为names集合
  2. 如果names不包含"-default",则遍历cachedActivates注解缓存,如果group与Activate注解的group匹配,则按照name获取扩展实现,如果names不包含cachedActivates的key(即:注解的value属性的值,对应spi的name=value),并且names不包含"-name",并且 Activate注解value属性为空 或者 URL的parameters中包含Activate注解的value属性中的一个配置不为空,则将扩展添加至exts集合
  3. 遍历names集合,如果name不是"-“开头,并且names不包含”-name",如果name是"default",则usrs扩展实现列表添加至exts扩展实现列表的头部并清空usrs(避免后面usrs往exts中添加时重复添加),否则根据name获取扩展实现添加至usrs;如果usrs不为空,则添加至exts中,返回扩展实现列表exts

3. 服务提供者 dubbo provider

在这里插入图片描述

ServiceConfig.export 导出暴露服务
  1. 如果providerConfig不为空,则判断provider是否已经暴露过,是则返回,如果delay为空,获取配置中的delay,启动一个线程(DelayExportServiceThread)使用Thread.sleep延迟调用doExport
ServiceConfig.doExport step1
  1. interfaceName接口必须配置不能为空:"<dubbo:service interface="" /> interface not allow null!"
  2. checkDefault:如果providerConfig为空新建,并且填入默认参数appendProperties
  1. appendProperties开始
  2. 注入方法使用配置属性的set方法,并且必须是public,入参个数为1并且是原始类型
  3. 从System中获取配置赋值给本地value,参数命名规则:dubbo.类名称去除Config,Bean后小写,例如:ProviderConfig=provider.[如果有配置id则增加id].方法名称去除set并小写
  4. 如果本地value依然为空,则尝试从实例中调用get或is方法获取属性判断属性是否获取的值为空,如果是,则再次尝试从System中按照前一步那样获取配置属性,如果为空则尝试读取:dubbo.properties.file配置指定的配置文件中的配置,如果没有配置则使用默认配置文件:dubbo.properties
  5. 如果本地value依然为空,判断当前属性是否属于legacyProperties集合中,如果是将其对应的key按照前两步步骤获取并赋值给本地value
  6. appendProperties完成
  1. 调用set方法将value赋值
ServiceConfig.doExport step2
  1. ProviderConfig如果不为空,从ProviderConfig中为application,module,registries,monitor,protocols赋值
  2. ModuleConfig如果不为空,从ModuleConfig中为registries,monitor赋值
  3. ApplicationConfig如果不为空,从ApplicationConfig中为registries,monitor赋值
  4. 如果ref实现类是GenericService类型,则标识generic=true,interfaceClass = GenericService.class(通用服务接口),否则反射获取类型为interfaceClass赋值,校验接口与方法:checkInterfaceAndMethods,校验实现:checkRef,标识generic=false
  5. local如果不为空并且为true,则覆写为interfaceName+“Local”,校验interfaceClass是否为local接口的实现
  6. stub如果不为空并且为true,则覆写为interfaceName+“Stub”,校验interfaceClass是否为stub接口的实现
  7. checkApplication同checkDefault
  8. checkRegistry
  9. checkProtocol
  10. appendProperties(this) 为当前实例赋值
  11. checkStubAndMock
  12. 如果path为空则为其赋值:interfaceName;
  13. doExportUrls导出Urls
doExportUrls 将提供者导出为URL路径
  1. loadRegistries从注册中心加载URL路径列表,url均设置为registry协议
  2. RegistryConfig获取address,如果为空则使用0.0.0.0
  3. 从System中获取dubbo.registry.address配置,如果存在则覆写address
  4. appendParameters从ApplicationConfig收集属性至map中
  1. appendParameters开始
  2. 如果是get,is方法,不包含getClass方法,public修饰,无参方法,返回类型为原始类型
  3. 获取Parameter注解,如果为excluded表示不需要收集该属性至map集合
  4. parameters map的key使用Parameter注解的key属性,如果不存在则使用方法名称小写,根据key从parameters
  5. 如果Parameter注解为escape则调用URLEncode.encode编译,如果Parameter注解为append,则从parameters map中获取之前的属性,如果存在使用逗号拼接,如果存在prefix,则添加前缀
  6. 将处理后的value属性放入parameters map 使用 key
  7. 如果方法名为getParameters,public修饰,无参方法,返回map,调用方法返回map,遍历map,将之前存在的key与新map的key使用"."拼接,value使用新map的value
  8. appendParameters完成
  1. appendParameters从ServiceConfig收集属性至map中
  2. 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()));
}
  1. 如果此时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
  1. parseURLs根据address,map解析组装URL集合
  1. parseURL开始
  2. 如果address包含"😕/"字符,则直接赋值给url
  3. 如果address不包含"😕/"字符,则使用逗号切分,第一个作为url,其余的如果存在则作为backup参数
  4. 从map集合中获取protocol协议,不存在则使用dubbo协议
  5. 根据url字符串获取URL对象,如果解析的URL对象的协议为空则使用default集合中的默认配置,用户名,密码,端口,path路径,参数集合同样如果为空使用默认值
  6. parseURL结束
  1. 遍历URLs集合,为每个URL的registry参数设置为url.getProtocol(),URL设置为registry协议,如果是provider并且register参数为true,或者,不是provider并且subscribe为true,则将url添加至注册URL列表中
  2. ProtocolConfig配置列表遍历导出URL:doExportUrlsFor1Protocol(protocolConfig, registryURLs);
doExportUrlsFor1Protocol 为单个协议导出provider服务URL
  1. ProtocolConfig协议名称没配置使用dubbo协议
  2. ProtocolConfig主机host没配置则使用ProviderConfig的host
  3. 如果host为无效的(localhost,0.0.0.0等),anyhost置为true,则创建socket与注册中心连接一次,通过socket获取本地地址。如果依然无效,使用工具包获取本地地址
  4. ProtocolConfig端口port没配置则使用ProviderConfig的port
  5. 如果端口依然为空或者0,则使用默认端口:根据协议name获取协议的实现类中的默认端口号,如:DubboProtocol默认端口:20880
  6. 如果端口依然为空,则随机获取当前本机可使用的端口号
  7. 创建map准备收集配置参数
  8. 如果anyhost=true则添加anyhost为true至map
  9. map中添加side,dubbo,时间戳,pid
  10. 收集各个配置属性
appendParameters(map, application);
appendParameters(map, module);
appendParameters(map, provider, Constants.DEFAULT_KEY);
appendParameters(map, protocolConfig);
appendParameters(map, this);
  1. map中添加方法属性,token令牌属性
  2. 导出服务
  3. 根据协议名称,主机,端口,路径,以及map参数列表创建URL
  4. 根据协议获取ConfiguratorFactory接口实现,如果有,则调用实现类configure方法处理url
override=com.alibaba.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
absent=com.alibaba.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
  1. 如果scope配置为none则为不暴露,local,remote,该属性可在ServiceConfig中指定,默认为null,即暴露本地也暴露远程
  2. 配置为none不暴露
  3. 配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
  4. 如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
ServiceConfig.exportLocal 本地暴露服务
  1. 仅将服务暴露在本地jvm中
ServiceConfig.export 远程暴露服务
  1. 如果注册中心URL列表不为空,并且服务URL的register参数为true(没配置则默认为true),遍历注册中心注册暴露服务
  2. 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&timestamp=1544673080385&logger=slf4j&pid=9892&registry=zookeeper&timestamp=1544673080385
  1. 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
  1. RegistryProtocol.doLocalExport 导出invoker
  1. doLocalExport 开始
  2. 从bouds缓存中获取,如果没有则导出并缓存
  3. 将originInvoker封装为InvokerDelegete委派对象,并将export服务url与委派对象绑定
  4. protocol.export 导出服务,此时的协议是用ExtensionLoader注入的适配类型,导出的invoker绑定的url是ProtocolConfig协议,如:DubboProtocol,导出后的包装链
graph LR
ExporterChangeableWrapper-->ListenerExporterWrapper
ListenerExporterWrapper-->DubboExporter
DubboExporter-->ProtocolFilterWrapper.Invoker
ProtocolFilterWrapper.Invoker-->InvokerDelegete
InvokerDelegete-->DubboInvoker

在这里插入图片描述

  1. doLocalExport 完成
  1. openServer服务端打开服务端口
  2. url参数判断isserver参数是否为true,默认为true
  3. serverMap缓存获取server,如果不存在创建服务
RegistryProtocol.export Registry协议导出服务 step2 createServer创建服务
  1. 默认开启server关闭时发送readonly事件
  2. 默认开启heartbeat,60秒
  3. 获取url参数server,默认为netty,ExtensionLoader判断该类型Transporter是否支持,不支持抛出异常
  4. Exchangers.bind(url, requestHandler)绑定端口提供服务
  5. Exchanger默认为HeaderExchanger(目前仅支持该类型)
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
  1. 执行Transporters绑定com.alibaba.dubbo.remoting.Transporters#bind(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.remoting.ChannelHandler…),如果是多个handler则将多个handler封装为ChannelHandlerDispatcher进行绑定
  2. HeaderExchangeServer构造器中启动心跳,如果url中没有配置,缺省为0,即不启动心跳
  3. 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);
}
  1. NettyServer构造器包装handler,并在url中指定线程池名称:DubboServerHandler
public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
    super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
  1. HeartbeatHandler封装的ChannelHandler句柄通过com.alibaba.dubbo.remoting.Dispatcher#dispatch返回。例如:AllChannelHandler
  2. 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

在这里插入图片描述

  1. handler链初始化完成,调用父类构造器:AbstractServer
  2. AbstractServer构造器中打开服务doOpen
  3. 调用子类NettyServer.doOpen打开服务,并将NettyServer与NettyHandler绑定,响应客户端请求
RegistryProtocol.export Registry协议导出服务 step3
  1. RegistryProtocol.getRegistry 根据原始invoker的URL获取注册中心,如:zk协议的ZookeeperRegistryFactory,创建zk注册中心:new ZookeeperRegistry(url, zookeeperTransporter);
  2. 初始化FailbackRegistry,默认5秒检查一次与注册中心的连接
  3. 初始化AbstractRegistry,notify通知注册中心的监听器
  4. 继续构造ZookeeperRegistry
  5. 缺省使用zkclinet连接zk注册中心,ZkclientZookeeperClient,添加zk状态监听器StateListener
  6. RegistryProtocol.getRegistedProviderUrl返回注册到注册中心的URL,对URL参数进行一次过滤
  7. 将dubbo服务url注册至注册中心,创建zk数据节点,如dubbo协议:dubbo://…
  8. getSubscribedOverrideUrl将协议设置为provider
  9. 创建provider url监听器OverrideListener
  10. registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);订阅注册中心的provider url
  11. 返回export
完成导出并将export缓存至ServiceConfig对象的exporters

4. 服务消费者 dubbo consumer

在这里插入图片描述

ReferenceConfig.get 获取服务引用
  1. 如果ref引用为空,则初始化init
ReferenceConfig.init 初始化
  1. checkDefault 如果ConsumerConfig配置为空,则创建并使用缺省配置
  2. appendProperties 使用系统配置填充ReferenceConfig
  3. 类似提供者,如果ConsumerConfig不为空,application、module、registries、monitor为空使用ConsumerConfig获取;其次使用module、application获取兜底配置
  4. 类似提供者,checkApplication,checkStubAndMock,组装默认配置集合map,其中attributes通过系统context进行存储(MethodConfig的属性),同样使用appendAttributes方法收集至attributes集合中
  5. 使用map默认配置创建代理对象
ReferenceConfig.createProxy 使用map默认配置创建代理对象
  1. 如果是injvm协议,则按照Injvm协议获取引用
  2. 如果ReferenceConfig配置了url则使用点对点方式拼接URL列表,url配置使用分号分隔
  3. 如果是注册中心方式,从注册中心拼装URL列表,类似提供者调用loadRegistries组装注册中心URL列表,URL协议均设置为registry
  4. 如果注册中心地址只有一个,则调用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&timestamp=1544767680215&registry=zookeeper&timestamp=1544767680267
  1. 如果注册中心地址存在多个,调用refprotocol.refer获取所有注册中心的invoker,并调用AvailableCluster的cluster.join负载均衡的方式选择一个invoker
  2. refprotocol适配类型,调用链
  3. 创建invoker完成后判断是否需要校验check,xml配置中对应check属性,默认check=true,通常配置check=false(即不校验)。如果开启了校验则会判断提供者是否存在(所有连接中存在一个已连接并且不为只读状态)
graph LR
ProtocolListenerWrapper-->ProtocolFilterWrapper.Invoker
ProtocolFilterWrapper.Invoker-->RegistryProtocol

在这里插入图片描述

RegistryProtocol.refer 获取服务invoker引用
  1. 根据注册中心url获取Registry实例,adaptive指定协议为key,即获取的是协议对应的注册中心实例,如:ZookeeperRegistryFactory.getRegistry返回ZookeeperRegistry,过程同提供者相同
  2. 如果服务是RegistryService类型,则proxyFactory.getInvoker((T) registry, type, url)获取invoker
  3. 如果refer参数对应的group属性存在,使用MergeableCluster获取invoker
  4. 缺省调用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&timestamp=1544702578695&timestamp=1544702578729
RegistryProtocol.doRefer 获取服务invoker引用 step1
  1. 创建注册中心目录对象RegistryDirectory
  2. RegistryDirectory设置注册中心Registry、协议Protocol(适配类型)
  3. 创建消费者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&timestamp=1544703002095
  1. 如果注册中心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&timestamp=1544703323067
  1. RegistryDirectory.subscribe 注册中心目录订阅,入参URL的category参数改为providers、routers、configurations目录即订阅改三个目录
  2. 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&timestamp=1544703743811
  1. 如果消费URL的接口interface参数为ANY_VALUE(即*),则订阅root(即/dubbo)目录下的所有子目录服务变更的消息
  2. FailbackRegistry回调父类结束调用钩子方法doSubscribe
  3. 如果url的interface属性为"*",zk创建root节点,并添加监听
  4. 如果url的interface属性不为"*",获取url目录属性(providers,configurators,routers),zk创建节点并监听
  1. 如果监听目录非空(即存在提供者)
  2. 如果没有提供者则使用empty协议,消费者与提供者匹配上,host使用消费者的host,side=consumer,放入urls
  3. 如果有提供者,消费者与提供者匹配上,放入urls
  4. 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&timestamp=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&timestamp=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&timestamp=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&timestamp=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));
  1. notify通知,回调父类notify,回调父类doNotify,如果失败,将失败的通知请求记录到失败列表,定时重试,回调父类notify,保存url配置并通知监听者RegistryDirectory.notify(包含重写的url:overrideDirectoryUrl,如果客户端没有配置timeout超时会继承提供者的timeout超时配置)
  2. RegistryDirectory.refreshInvoker刷新invoker
  3. 如果是empty协议,清空本地该服务的所有invoker引用
  4. 如果invokerUrls不为空(即存在提供者),toInvokers封装为Invoker缓存至urlInvokerMap,toInvokers返回Invoker包装
RegistryProtocol.doRefer toInvokers创建Invoker step2
  1. 如果缓存中不存在Invoker,则重新引用
按照提供者URL封装Invoker
invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
graph LR
InvokerDelegete-->ListenerInvokerWrapper
ListenerInvokerWrapper-->ProtocolFilterWrapper
ProtocolFilterWrapper-->DubboInvoker

在这里插入图片描述

  1. 根据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;
}
  1. getClients获取clients集合,如果参数connections没配置,默认设为0,代表共享连接,默认为共享连接。否则按照指定connections数量初始化多个连接
  1. getSharedClient缓存中获取,以ip:port为key缓存,获取如果不存在,则新建:initClient
  2. initClient 新建连接
  1. initClient 新建连接,根据lazy参数决定是否使用懒惰连接
  2. LazyConnectExchangeClient客户端连接,在实际调用的时候判断如果没有连接再创建。默认使用HeaderExchangeClient
  3. Exchangers.connect直接连接获取客户端,根据url的exchanger参数获取Exchange进行连接
  4. 目前只有一种实现HeaderExchanger
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
    return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
  1. Transporters.connect连接,ExchangeHandlerAdapter增加两层包装:DecodeHandler、HeaderExchangeHandler
graph LR
DecodeHandler-->HeaderExchangeHandler
HeaderExchangeHandler-->ExchangeHandlerAdapter

在这里插入图片描述

  1. 获取Transporter适配类型调用connect,默认走netty
  2. NettyTransporter.connect,返回NettyClient(url, listener)
public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException{
    super(url, wrapChannelHandler(url, handler));
}
  1. wrapChannelHandler继续包装监听handle
graph LR
MultiMessageHandler-->HeartbeatHandler
HeartbeatHandler-->AllChannelHandler
AllChannelHandler-->DecodeHandler

在这里插入图片描述

  1. 父类AbstractClient构造器中打开连接doOpen
  2. 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;
        }
    });
}
  1. 创建client完成,client注入DubboInvoker,创建Invoker完成
  2. Directory订阅完成
RegistryProtocol.doRefer 包装invoker引用 step3
  1. cluster.join(directory) 根据目录组装Invoker
graph LR
MockClusterWrapper-->FailoverCluster

在这里插入图片描述

  1. 再次包装Directory中包装的InvokerDelegete,FailoverClusterInvoker按照重试次数循环调用invoker
graph LR
MockClusterInvoker-->FailoverClusterInvoker
FailoverClusterInvoker-->InvokerDelegete

在这里插入图片描述

proxyFactory.getProxy(invoker) 创建服务代理
  1. proxy参数没有配置情况下,缺省StubProxyFactoryWrapper包装的JavassistProxyFactory创建代理
  2. StubProxyFactoryWrapper.getProxy,调用包装类的getProxy即适配类型的代理工厂,默认JavassistProxyFactory创建InvokerInvocationHandler得代理
  3. 创建完成返回代理对象

5. Invoker客户端调用服务端

在这里插入图片描述

1. MockClusterInvoker调用
  1. 如果方法是mock方法,生成mock方法,直接返回mock的返回值,不是则继续调用绑定的invoker
2. FailoverClusterInvoker 调用
  1. list列举所有invokers
  2. 负载均衡获取invoker,默认rundom随机
  3. 按照负载均衡算法获取invoker调用
  4. 如果失败了根据重试次数重试(循环的方式)
3. InvokerDelegete 调用
  1. 直接调用绑定的invoker
4. ListenerInvokerWrapper
  1. 构造器中将invoker回调Listener.referred通知监听器
  2. invoker直接调用绑定的invoker
ProtocolFilterWrapper.Invoker 内部类调用
  1. 通过Filter链调用下层Invoker(DubboInvoker),filter.invoke(next, invocation);
DubboInvoker调用
  1. invoker调用子类doInvoker方法,调用是顺序遍历的方式选择客户端,客户端缓存是按照ip:port为key
ExchangeClient currentClient;
if (clients.length == 1) {
    currentClient = clients[0];
} else {
    currentClient = clients[index.getAndIncrement() % clients.length];
}
  1. 如果是oneWay则只负责发送后直接返回一个空的RpcResult,调用currentClient.send
  2. 如果是Async,调用currentClient.request,同样发送后返回一个空的RpcResult,将异步调用的ResponseFuture与回调动作FutureAdapter绑定到RpcContext的ThreadLocal
  3. 如果是同步的则同步等待RpcResult,调用currentClient.request
currentClient.request 客户端发送调用请求

按照非懒惰方式客户端梳理

  1. HeaderExchangeClient.request 发送请求
  2. 委派HeaderExchangeChannel.request 发送send请求,如果消息是Request,Response,String类型之一,直接发送,否则封装为Request类型发送
  3. HeaderExchangeChannel.channel(NettyClient)链调用send,HeaderExchangeChannel.request请求前创建DefaultFuture实例监控超时,DefaultFeature.RemotingInvocationTimeoutScan扫描超时服务,DubboResponseTimeoutScanTimer线程每30毫秒扫描一次
  4. NettyClient.send 发送消息
  5. getChannel获取通道发送消息
  6. 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);
}
  1. 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协议为通信进行梳理

  1. 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

在这里插入图片描述

  1. MultiMessageHandler:如果消息是MultiMessage类型,则遍历调取后续的handler链,否则直接调用下一节点
  2. HeartbeatHandler:添加时间戳属性READ_TIMESTAMP,如果消息是Request并且是心跳Request,则判断是否twoWay模式,如果是,则封装Response发送心跳事件Response.HEARTBEAT_EVENT,如果消息是心跳的Response则打印日志,继续调用下一节点
  3. AllChannelHandler:将消息接收任务封装为ChannelEventRunnable线程放入线程池中处理,线程池执行直接调用下一节点
  4. DecodeHandler:如果消息是Decodeable类型,则反编译消息,继续调用下一节点
  5. HeaderExchangeHandler
  1. 如果是Request类型,如果是事件消息,则handle事件(判断是否READONLY事件,如果是,为Channel设置READONLY,否则,如果是twoway,判断请求data是否为空或者异常,是则直接响应Response,调用下一节点并响应Response;如果不是twoway直接调用下一节点
  2. 如果是Response类型,如果是心跳,则处理心跳消息
  3. 如果是String类型,则调用下一节点的telnet方法处理
  4. 兜底直接调用下一节点received
  1. 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);
    }
}
  1. HeaderExchangeHandler如果是twoway,将DubboProtocol.ExchangeHandlerAdapter返回的Result封装为Response响应回客户端
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值