美文网首页dubbo
Dubbo之服务引用源码分析

Dubbo之服务引用源码分析

作者: loveFXX | 来源:发表于2020-08-04 09:25 被阅读0次

    服务引用有两种:
    1、直连方式引用
    2、基于注册中心引用

    引用服务时机

    1、 ReferenceBean 的 afterPropertiesSet 方法时引用服务
    org.apache.dubbo.config.spring.ReferenceBean#afterPropertiesSet
    由于ReferenceBean实现了InitializingBean,这里是spring bean生命周期中,调用afterPropertiesSet执行初始化方法的一个切入点。


    image.png

    调用shouldInit方法查看init参数是否为true,如果为true将会在这里初始化。默认是false
    2、 ReferenceBean 对应的服务提供者类被注入到服务消费者类中的引用(默认情况)
    org.apache.dubbo.config.ReferenceConfig#get
    其主要逻辑是在init方法,经历获取服务配置、Invoker 创建、代理类创建等步骤。


    image.png

    init

    org.apache.dubbo.config.ReferenceConfig#init


    image.png

    1、检查本地存根checkStubAndLocal
    2、检查Mock checkMock


    image.png
    3、添加 side、协议版本信息、时间戳和进程号等信息到 map 中,如果不是泛化服务将获取版本,获取接口方法列表,并添加到 map 中,将 ApplicationConfig、ConsumerConfig、ReferenceConfig 等对象的字段信息添加到 map 中
    image.png

    4、methods参数配置处理,


    image.png
    5、获取服务消费者 ip 地址、存储 attributes 到系统上下文中
    image.png
    6、调用createProxy创建代理类ref

    引用服务

    org.apache.dubbo.config.ReferenceConfig#createProxy


    image.png

    1、本地JVM引用


    image.png
    2、url不为空,则是点对点的服务地址。@Reference中指定了url属性
    image.png

    3、如果是注册中心地址,则在url中添加一个refer参数。
    首先获取注册url。然后把引入的map数据属性放到refer后与注册url拼接,并添加到urls中。
    如果是服务地址,有可能url中配置了参数,map中表示的服务消费者消费服务时的参数,这里所以需要合并mergeUrl

    registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-consumer-application&dubbo=2.0.2&pid=9484&refer=application%3Ddubbo-demo-consumer-application%26dubbo%3D2.0.2%26group%3Dg1%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D9484%26register.ip%3D192.168.1.2%26release%3D2.7.0%26revision%3D1.1.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1596342012598%26version%3D1.1.1&registry=zookeeper&release=2.7.0&timestamp=1596342266342
    
    image.png

    4、@Reference中的protocol属性表示使用哪个协议调用服务,如果不是本地调用协议,则把注册中心地址找出来
    如果只有一个注册服务url,则直接调用Protocol的自适应类,调用ref引用方法,这里会调用到RegistryProtocol、DubboProtocol


    image.png

    5、如果有多个注册服务url,则会进行遍历urls。生成的执行体invoker添加到invokers集合中。并对最后一个registryURL,进行Cluster包裹


    image.png
    6、得到执行体invoker,并通过ProxyFactory生成代理类
    生成的invoker实例,就是为了调用。这里最终的包裹
    image.png
    MockClustInvoker包裹服务字典RegistryDirectory和集群容错Cluster的FailoverClusterInvoker,FailoverClusterInvoker也包裹了服务字典RegistryDirectory

    所以说,在根据PROXY_FACTORY.getProxy获取invoker的代理类之前,生成invoker才是代理类的核心。

    动态服务目录

    RegistryProtocol的包裹类ProtocolFilterWrapper和ProtocolListenerWrapper(最外包裹)就不看了,也是利用SPI
    1、refer
    org.apache.dubbo.registry.integration.RegistryProtocol#refer


    image.png

    ①、把url地址由registry://开头转换成 zookeeper://开头

    zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-consumer-application&dubbo=2.0.2&pid=10732&refer=application%3Ddubbo-demo-consumer-application%26dubbo%3D2.0.2%26group%3Dg1%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D10732%26register.ip%3D192.168.1.2%26release%3D2.7.0%26revision%3D1.1.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1596345426916%26version%3D1.1.1&release=2.7.0&timestamp=1596345429789
    

    ②、从url获取注册类registry


    image.png

    ③、 qs表示 查询字符, 表示url中的参数,表示消费者引入服务所配置的参数。如果有group有多个值,则这里的cluster为MergeableCluster
    ④、doRefer方法调用
    2、doRefer
    org.apache.dubbo.registry.integration.RegistryProtocol#doRefer


    image.png
    image.png
    ①、创建动态服务目录动态RegistryDirectory,并设置registry、protocol值。RegistryDirectory实现了 NotifyListener 接口。当注册中心服务配置发生变化后,RegistryDirectory 可收到与当前服务相关的变化。收到变更通知后,RegistryDirectory 可根据配置变更信息刷新 Invoker 列表。在服务消费端最核心的就是这个服务目录

    ②、获取订阅subscribeUrl、并注册到zookeeper

    consumer://192.168.1.2/org.apache.dubbo.demo.DemoService?application=dubbo-demo-consumer-application&dubbo=2.0.2&group=g1&interface=org.apache.dubbo.demo.DemoService&lazy=false&methods=sayHello&pid=10732&release=2.7.0&revision=1.1.1&side=consumer&sticky=false&timestamp=1596345426916&version=1.1.1
    

    a、register
    org.apache.dubbo.registry.support.FailbackRegistry#register


    image.png

    b、doRegister
    org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doRegister


    image.png
    注册路径是:
    /dubbo/org.apache.dubbo.demo.DemoService/consumers/consumer%3A%2F%2F192.168.1.2%2Forg.apache.dubbo.demo.DemoService%3Fapplication%3Ddubbo-demo-consumer-application%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.2%26group%3Dg1%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D10732%26release%3D2.7.0%26revision%3D1.1.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1596345426916%26version%3D1.1.1
    

    ③、构建路由链
    ④、服务目录需要订阅的几个路径
    ⑤、cluster包裹服务目录

    构建路由链

    路由链是动态服务目录的一个属性值,在引入服务时可以根据路由条件进行过滤
    org.apache.dubbo.registry.integration.RegistryDirectory#buildRouterChain


    image.png

    org.apache.dubbo.rpc.cluster.RouterChain#buildChain


    image.png
    org.apache.dubbo.rpc.cluster.RouterChain#RouterChain
    image.png
    根据路由工厂RouterFactory接口的扩展实现类,得到四个:

    MockRouterFactory、TagRouterFactory 标签路由、AppRouterFactory应用条件路由、ServiceRouterFactory服务条件路由
    利用RouterFactory根据url生成各个类型的Router、并把routers按priority进行排序
    MockInvokersSelector(priority=负最大整数)、TagRouter(priority=100)、ServiceRouter(priority=140)、AppRouter(priority=150)

    服务目录订阅路径

    服务目录需要订阅的几个路径
    服务提供者目录:/dubbo/org.apache.dubbo.demo.DemoService/providers
    老版本动态配置目录:/dubbo/org.apache.dubbo.demo.DemoService/configurators
    老版本路由器目录:/dubbo/org.apache.dubbo.demo.DemoService/routers
    0、subscribe
    org.apache.dubbo.registry.integration.RegistryDirectory#subscribe


    image.png

    此时有url多了category=providers,configurators,routers参数

    consumer://192.168.1.2/org.apache.dubbo.demo.DemoService?application=dubbo-demo-consumer-application&category=providers,configurators,routers&dubbo=2.0.2&group=g1&interface=org.apache.dubbo.demo.DemoService&lazy=false&methods=sayHello&pid=10224&release=2.7.0&revision=1.1.1&side=consumer&sticky=false&timestamp=1596347742946&version=1.1.1
    

    1、ConsumerConfigurationListener监听器,
    ConsumerConfigurationListener是RegistryDirectory的一个属性值,在创建RegistryDirectory对象时创建


    image.png

    key是dubbo-demo-consumer-application.configurators,所以监听应用动态配置路径是/dubbo/config/dubbo/dubbo-demo-consumer-application.configurators
    2、ReferenceConfigurationListener监听器


    image.png
    key是org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators,所以监听路径服务动态配置目录是/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators
    3、subscribe
    org.apache.dubbo.registry.support.FailbackRegistry#subscribe
    image.png

    ①、subscribe
    org.apache.dubbo.registry.support.FailbackRegistry#subscribe


    image.png
    ②、doSubscribe
    org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe
    a、首先判断服务接口是不是*,这里不会走
    image.png
    image.png
    b、走else逻辑
    从消费者url根据category获取路径(category=providers,configurators,routers)
    image.png
    得到服务提供者端配置路径(providers服务提供者、configurators服务提供者配置路径、routers路由配置路径)
    /dubbo/org.apache.dubbo.demo.DemoService/providers
    /dubbo/org.apache.dubbo.demo.DemoService/configurators

    /dubbo/org.apache.dubbo.demo.DemoService/routers


    image.png
    对下面三个路径进行遍历,并创建路径,在添加urls之前,会首先通过toUrlsWithEmpty进行Empty协议替换。
    image.png
    c、toUrlsWithEmpty
    org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#toUrlsWithEmpty
    为空,则用
    image.png
    d、toUrlsWithoutEmpty
    对引入/dubbo/org.apache.dubbo.demo.DemoService/providers所有服务提供者进行遍历
    org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#toUrlsWithoutEmpty
    image.png
    e、isMatch
    org.apache.dubbo.common.utils.UrlUtils#isMatch
    服务提供者与消费者引入是否匹配
    image.png
    首先根据consumerInterface和providerInterface
    image.png
    根据category、根据enabled,默认是true
    consumerUrl中category=providers,configurators,routers
    providerUrl 得到默认category=providers
    consumerUrl = "consumer://192.168.1.2/org.apache.dubbo.demo.DemoService?application=dubbo-demo-consumer-application&category=providers,configurators,routers&dubbo=2.0.2&group=g1&interface=org.apache.dubbo.demo.DemoService&lazy=false&methods=sayHello&pid=9320&release=2.7.0&revision=1.1.1&side=consumer&sticky=false&timestamp=1596349730827&version=1.1.1"
    providerUrl =  "dubbo://192.168.1.2:20881/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider1-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService:1.1.1:g1&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=g1&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=9596&release=2.7.0&revision=1.1.1&sayHello.loadbalance=random&sayHello.return=true&side=provider&timeout=3000&timestamp=1596340240853&version=1.1.1"
    

    根据group、version、classifier。三个同时符合则返回true


    image.png

    ③、notify
    org.apache.dubbo.registry.support.FailbackRegistry#notify


    image.png
    a、doNotify
    org.apache.dubbo.registry.support.FailbackRegistry#doNotify
    image.png

    b、notify
    org.apache.dubbo.registry.support.AbstractRegistry#notify
    分类category


    image.png
    image.png
    c、对routers、configurators、providers调用notify方法
    org.apache.dubbo.registry.integration.RegistryDirectory#notify
    image.png
    获取动态配置URL,生成configurators、获取老版本路由URL,生成Router,并添加到路由链中、获取服务提供者URL
    d、refreshOverrideAndInvoker
    这里只要看providers,根据动态配置手动触发一次刷新invoker。
    org.apache.dubbo.registry.integration.RegistryDirectory#refreshOverrideAndInvoker
    image.png

    e、refreshInvoker
    org.apache.dubbo.registry.integration.RegistryDirectory#refreshInvoker
    这里会Protocol进行过滤,并且调用DubboProtocol.refer方法得到Invoker


    image.png
    image.png
    得到的newInvokers设置到RegistryDirectory的routerChain和invokers中
    f、toInvokers
    org.apache.dubbo.registry.integration.RegistryDirectory#toInvokers
    遍历当前服务所有的服务提供者URL、当前消费者如果手动配置了Protocol,那么则进行匹配
    image.png
    当前Protocol是否在应用中存在对应的扩展点
    image.png
    如果当前服务提供者URL缓存中localUrlInvokerMap没有,则之前没有生产过Invoker。则会调用Protocol的refer方法生成一个Invoker
    image.png
    会调用到DubboProtocol#refer方法,DubboProtocol继承了AbstractProtocol,DubboProtocol不存在refer方法。会调用到父类AbstractProtocol的refer方法
    客户端ExchangeClient创建
    DubboInvoker创建

    1、refer
    org.apache.dubbo.rpc.protocol.AbstractProtocol#refer
    DubboInvoker是异步的,而AsyncToSyncInvoker会封装为同步的


    image.png

    2、protocolBindingRefer
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#protocolBindingRefer


    image.png
    通过getClients(url)方法获取ExchangeClient客户端。这里是个数组形式, 在DubboInvoker发送请求时会轮询clients去发送数据,如果有多个的话。ExchangeClient 实际上并不具备通信能力,它需要基于更底层的客户端实例进行通信例如NettyClient
    3、getClients
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients
    image.png

    connections表示建立几个socket连接,在DubboProtocol中,每构造一个Client就会去和Server建立一个Socket连接
    如果connections为0,这时会去获取shareconnections参数,默认为1。默认情况下,使用共享客户端实例。如果不使用共享客户端会调用initClient方法,getSharedClient内部也会调用initClient
    4、getSharedClient
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#getSharedClient


    image.png
    image.png
    image.png
    5、buildReferenceCountExchangeClientList
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#buildReferenceCountExchangeClientList
    image.png
    6、buildReferenceCountExchangeClient
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#buildReferenceCountExchangeClient
    image.png
    initClient生成一个ExchangeClient,并用ReferenceCountExchangeClient包装。ReferenceCountExchangeClient是引用计数功能的 ExchangeClient
    7、initClient
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#initClient
    image.png

    从url得到client值,默认为netty。添加编解码和心跳包参数到 url 中,编码方式是DubboCodec.NAME、心跳heartbeat值是60 * 1000ms。并检测客户端类型是否存在,不存在则抛出异常。


    image.png
    获取 lazy 配置,并根据配置值决定创建的客户端类型,LazyConnectExchangeClient
    Exchangers建立连接,把url和requestHandler传入。requestHandler是DubboProtocol的内部类
    8、connect
    org.apache.dubbo.remoting.exchange.Exchangers#connect
    得到一个HeaderExchanger(Exchanger的SPI默认是header)去connect
    image.png
    9、connect
    org.apache.dubbo.remoting.exchange.support.header.HeaderExchanger#connect
    这里进行以下调用,分别如下:
    创建HeaderExchangeHandler 对象、 创建 DecodeHandler 对象、通过 Transporters 构建 Client 实例、 创建 HeaderExchangeClient 对象。
    用NettyTransporter去connect。DecodeHandler解码,是把InputStream解析成AppResponse对象
    image.png
    10、connect
    org.apache.dubbo.remoting.Transporters#connect
    image.png
    11、connect
    org.apache.dubbo.remoting.transport.netty4.NettyTransporter#connect
    image.png
    12、NettyClient
    org.apache.dubbo.remoting.transport.netty4.NettyClient#NettyClient
    初始化和开始netty
    image.png
    13、AbstractClient
    org.apache.dubbo.remoting.transport.AbstractClient#AbstractClient
    image.png
    14、doOpen
    org.apache.dubbo.remoting.transport.netty4.NettyClient#doOpen
    netty启动
    image.png
    15、connect

    org.apache.dubbo.remoting.transport.AbstractClient#connect


    image.png
    16、doConnect
    org.apache.dubbo.remoting.transport.netty4.NettyClient#doConnect
    image.png
    所以得到的DubboInvoker结构是
    image.png
    MockClusterInvoker
     RegistryDirectory
      Collections$UnmodifiableRandomAccessList
     routerChain
      MockInvokersSelector
      TagRouter
      AppRouter
       ServiceRouter

    Collections$UnmodifiableRandomAccessList
     RegistryDirectory$InvokerDelegate
      ListenerInvokerWrapper
       ProtocolFilterWrapper$CallbackRegistrationInvoker
        ProtocolFilterWrapper$1
         AsyncToSyncInvoker
          DubboInvoker

    DubboInvoker
     ExchangeClient[1]
      ReferenceCountExchangeClient
       HeaderExchangeClient
        NettyClient

    创建代理

    PROXY_FACTORY.getProxy(invoker)
    1、getProxy
    org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper#getProxy(org.apache.dubbo.rpc.Invoker<T>)


    image.png

    2、getProxy
    org.apache.dubbo.rpc.proxy.AbstractProxyFactory#getProxy(org.apache.dubbo.rpc.Invoker<T>)


    image.png
    3、getProxy
    org.apache.dubbo.rpc.proxy.AbstractProxyFactory#getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
    image.png
    image.png

    4、getProxy
    org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getProxy


    image.png
    invoker通过InvokerInvocationHandler包装 最终生成的代理类
    这里也是调用的开始,通过sayHello方法会执行InvocationHandler handler的invoke方法
    package org.apache.dubbo.common.bytecode;
    
    public class proxy0 implements org.apache.dubbo.demo.DemoService {
    
        public static java.lang.reflect.Method[] methods;
    
        private java.lang.reflect.InvocationHandler handler;
    
        public proxy0() {
        }
    
        public proxy0(java.lang.reflect.InvocationHandler arg0) {
            handler = $1;
        }
        public java.lang.String sayHello(java.lang.String arg0) {
            Object[] args = new Object[1];
            args[0] = ($w) $1;
            Object ret = handler.invoke(this, methods[0], args);
            return (java.lang.String) ret;
        }
    }
    

    总结

    本片主要分析了dubbo消费端的从注册中心引入的过程。需要明白一点,引入是为调用做准备的。
    引入层级较为多,其核心思想就是Invoker的生成。最终ref引入的是Invoker经过InvokerInvocationHandler包装的代理类,通过Javassist生成。
    其中,Invoker是通过Protocol.ref生成。具体Protocol通过SPI原理实现,首先经过RegistryProtocol#refer、再经过DubboProtocol#refer方法。
    在RegistryProtocol中主要是生成动态服务目录RegistryDirectory,并为RegistryDirectory设置registry、protocol、消费url等,并设置路由链。由于服务目录实现了NotifyListener,是个监听器。需要对新老版本的服务动态配置路径及服务配置路径、路由等的监听。动态服务目录RegistryDirectory订阅后,会直接触发一次,这次会调用到DubboProtocol的refer方法。最终得到的RegistryDirectory需要cluster进行包装。
    在DubboProtocol引入中,主要是对客户端实例的获取。涉及到Exchanger 、传输协议Transporter等SPI,最终会调用NettyClient创建netty(默认)客户端,并进行连接。对DubboInvoker包装主要是AsyncToSyncInvoker

    相关文章

      网友评论

        本文标题:Dubbo之服务引用源码分析

        本文链接:https://www.haomeiwen.com/subject/ijjurktx.html