2、Dubbo源碼解析--服務發布原理(Netty服務暴露)


一、服務發布 - 原理:

首先看Dubbo日志,截取重要部分:

  1)暴露本地服務

    

Export dubbo service com.alibaba.dubbo.demo.DemoService to local registry, dubbo version: 2.0.0, current host: 10.165.2.47

 

  2)暴露遠程服務

    

Export dubbo service com.alibaba.dubbo.demo.DemoService to url dubbo://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.165.2.47&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&qos.port=22222&side=provider&timestamp=1520303067433, dubbo version: 2.0.0, current host: 10.165.2.47


Register dubbo service com.alibaba.dubbo.demo.DemoService url dubbo://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=10.165.2.47&bind.port=20880&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&qos.port=22222&side=provider&timestamp=1520303067433 to registry registry://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=3880&qos.port=22222&registry=multicast&timestamp=1520303067418, dubbo version: 2.0.0, current host: 10.165.2.47

 

  3)啟動Netty

     

Start NettyServer bind /0.0.0.0:20880, export /10.165.2.47:20880, dubbo version: 2.0.0, current host: 10.165.2.47

 

  4)打開Zookeeper

    

INFO zookeeper.ClientCnxn: Opening socket connection to server /192.168.48.117:2181

 

  5)注冊到Zookeeper

    

Register: dubbo://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider&timestamp=1520303067433, dubbo version: 2.0.0, current host: 10.165.2.47

 

  6)監聽Zookeeper

    

Subscribe: provider://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider&timestamp=1520303067433, dubbo version: 2.0.0, current host: 10.165.2.47

  7)頻繁發送廣播包,注冊中心利用廣播包監聽provider健康狀況

[DUBBO] Send broadcast message: subscribe provider://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider&timestamp=1520303067433 to /224.5.6.7:1234, dubbo version: 2.0.0, current host: 10.165.2.47

[DUBBO] Receive multicast message: subscribe provider://10.165.2.47:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3880&side=provider&timestamp=1520303067433 from /10.165.2.47:1234, dubbo version: 2.0.0, current host: 10.165.2.47

 

 

二、根據原理分析源碼

  1)首先看Provider注冊文件:

    

<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>

<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

 

    可以看到,是通過dubbo的schema service進行注入的,那我們找到DubboNameSpaceHandler,Dubbo命名空間處理器,找到<dubbo:service>標簽解析行:

 public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        //這里就是<dubbo:service>標簽的解析注入入口
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser(
"reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }

  

  2)上面new DubboBeanDefinitionParser是<dubbo:service>解析注入入口,那ServiceBean.class就是<dubbo:service>標簽的發布訂閱入口,進入ServiceBean可以看到一個核心方法onApplicationEvent(),其中export()就是發布方法:

   

public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isDelay() && !isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
        //這里是發布方法  
        export();
        }
    }    

  3)發布代碼處理路徑

  

ServiceBean.onApplicationEvent
-->export()
    -->ServiceConfig.export()
        -->doExport()
            -->doExportUrls() //里面有個for循環,代表一個服務可以有多個通信協議,例如tcp、http、dubbo等協議,默認是tcp協議
                -->loadRegistries(true)   //加載注冊信息,組裝注冊中心url信息,如源碼中config.properties中讀取dubbo.registry.address = zookeeper://192.168.99.100:32770鏈接信息,組裝Provider注冊鏈接串
                    -->doExportUrlsFor1Protocol(protocolConfig, registryURLs)
              // export to local if the config is not remote (export to remote only when config is remote)遠程暴露
              -->exportLocal(url) 
                -->proxyFactory.getInvoker(ref, (Class) interfaceClass, local))
                  -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist");
                  -->extension.getInvoker(arg0, arg1, arg2)
                    -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
                      -->proxyFactory.getInvoker(proxy, type, url)
                        -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
                          -->Wrapper.getWrapper(com.alibaba.dubbo.demo.provider.DemoServiceImpl)
                            -->makeWrapper(Class<?> c)
                          -->return new AbstractProxyInvoker<T>(proxy, type, url)
              -->protocal.export //本地暴露
                -->Protocal$Adaptive.export
                  -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocal.class).getExtension("injvm");
                  -->extension.export(arg0)
                    -->ProtocalFilterWrapper.export
                       -->buildInvokerChain //創建8個Filter
                        -->ProtocalListenerWrapper.export
                          -->InjvmProtocal.export
                            -->return new InjvmExporter
                               -->InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {super(invoker);
                                   this.key = key;
                                  this.exporterMap = exporterMap;
                                  exporterMap.put(key, this); //key=com.alibaba.dubbo.demo.DemoService this=InjvmExporter
                                 }

              
// export to local if the config is not remote (export to remote only when config is remote)遠程暴露
              -->proxyFactory.getInvoker()原理和本地暴露一樣都是為了獲取一個Invoker對象
                -->protocal.export
                  -->Protocal$Adaptive.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocal.class).getExtension("register");
                  -->extension.export(arg0)
                    -->ProtocalFilterWrappter.export
                      -->ProtocalListenerWrapper.export
                        -->RegistryProtocal.export
                          -->doLocalExport(originInvoker)
                            -->getCacheKey(originInvoker) //讀取dubbo://192.168.100.51:20880/
                            -->protocal.export
                              -->Protocol$Adaptive.export
                                -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocl.class).getExtension("dubbo");
                                -->extension.export(arg0)
                                  -->ProtocolFilterWrapper.export
                                    -->buildInvokerChain//創建8個Filter
                                     -->ProtocalListenerWrapper.export
                -------1、netty服務暴露的開始-------- -->DubboProtocal.export
                                        -->serviceKey(url) //組裝key=com.alibaba.dubbo.demo.DemoService:20880
                                        -->目的:exporterMap.put(key, this); //key=com.alibaba.dubbo.demo.DemoService:20880 this=
                                        -->openServer(url)
                                          -->createServer(url)
                --------2、信息交換層exchange 開始--------     -->Exchanges.bind(url, requestHandler) //exchanger是一個信息交換層
                                              -->getExchanger(url)
                                                -->getExchange(type)
                                                  -->ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension("header")
                                                    -->HeaderExchanger.bind
                                                      -->Transporters.bind(url, new DecoderHandler(new HeaderExchangeHandler(handler)))
                                                        -->new HeaderExchangeHandler(handler) //this.handler = handler
                                                          -->
new DecoderHandler
                                                            -->new AbstractChannelHandlerDelegate //this.handler = handler;
                ------------>3、網絡傳輸層 transporter ------------          -->Transporters.bind
                                                            -->getTransporter
                                                              -->
ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
                                                            -->Transporter$Adaptive.bind
                                                                //netty服務暴露
                                                              -->ExtensionLoader.getExtensionLoader(com.alibaba.remoting.Transporter.class).getExtension("netty")
                                                              -->extension.bind(arg0, arg1)
                                                                -->NettyTransporter.bind
                                                                  -->new NettyServer(url, listener)
                                                                    -->AbstractPeer //this.url = url; this.handler = handler;
                                                                    -->AbstractEndpoint //codec timeout=1000 connectTimeout=3000
                                                                    -->AbstractServer //bindAddress accepts=0 idleTimeout=600000
                ----------->4、打開端口,暴露netty服務 --------------                      -->doOpen()
                                                                      -->設置NioServerSocketChannelFactory boss worker的線程池 線程個數為3
                                                                      -->設置編碼handler
                                                                      -->bootstrap.bind(getBindAddress())
                                                            -->new HeaderExchangeServer()
                                                              -->this.server = NettyServer
                                                              -->this.heartbeat=600000
                                                              -->heartbeatTimeout=180000
                                                              -->startHeatbeatTimer() //這是一個心跳定時器,采用線程池ScheduledExecutePool,如果斷開了就心跳重連
                                                                -->

                                                                
                                                               
                   
 
 
  


說說上述這些類及方法的概念作用:”
  1、proxyFactory:就是為了獲取一個接口的代理類,例如獲取一個遠程接口的代理。
  它有2個方法,代表2個作用
    a、getInvoker:針對server端,將服務對象,例如DemoServiceImpl包裝成一個Wrapper對象。
    b、getProxy:針對client端,創建接口的代理對象,例如DemoService的接口。

  2、makeWrapper:它類似spring的BeanWrapper,它就是包裝了一個接口或一個類,可以通過Wrapper對實例對象進行賦值取值以及指定方法的調用。

  3、Invoker:它是一個可執行的對象,能夠根據方法名稱、參數得到相應的執行結果。
    它里面有一個很重要的方法Result invoke(Invocation invocation),Invocation是包含了需要執行的方法和參數的重要信息,目前它只有2個實現類RpcInvocation MockInvocation
    它有3種類型的Invoker
      1、本地執行類的Invoker
      2、遠程通信類的Invoker
      3、多個遠程通信執行類的Invoker聚合成集群版的Invoker
  4、Protocol:
    1)export暴露遠程服務(用語服務端),就是將proxyFactory.getInvoker創建的代理類invoker對象,通過協議暴露給外部。
    2)refer:引用遠程服務(用於客戶端)
  
  5、Exporter:維護invoker的生命周期

  6、exchanger:信息交換層,封裝請求響應模式同步轉異步

  7、transporter:網絡傳輸層,用來抽象Netty(dubbo默認)或者Mina的統一接口

   4)暴露本地服務與暴露遠程服務的區別是什么?

    a、暴露本地服務:指暴露在同一個JVM里面,不用通過zk來進行遠程通信,例如在同一個服務里面(tomcat),自己調用自己的接口,就無需進行網絡IP連接通信。

    b、暴露遠程服務:指暴露給遠程客戶端的IP和端口號,通過網絡來實現通信。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM