Dubbo源码学习笔记 之 服务发布

     学习了ExtensionLoader 之后,就开始进入服务发布与调用源码,个人先阅读的 服务发布。

  一. 服务发布的流程总览

     服务发布,实际的执行,是在ServiceBean的父类 ServiceConfig 里面的export()方法. 大体来说,包括以下几个步骤:

   1. 检查校验相关配置     

 public synchronized void export() {
        checkAndUpdateSubConfigs(); //进行检查、启动配置中心等

        if (!shouldExport()) {
            return;
        }

        if (shouldDelay()) {
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            doExport();
        }
    }

  

 检查更新配置的具体方法:

public void checkAndUpdateSubConfigs() {
        // Use default configs defined explicitly on global configs
        completeCompoundConfigs();
        // Config Center should always being started first.
        startConfigCenter();   //启动ConfigCenter
        checkDefault();
        checkProtocol();
        checkApplication();
        // if protocol is not injvm checkRegistry
        if (!isOnlyInJvm()) {
            checkRegistry();
        }
        this.refresh();
        checkMetadataReport();

        if (StringUtils.isEmpty(interfaceName)) {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }

        if (ref instanceof GenericService) {
            interfaceClass = GenericService.class;
            if (StringUtils.isEmpty(generic)) {
                generic = Boolean.TRUE.toString();
            }
        } else {
            try {
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            checkInterfaceAndMethods(interfaceClass, methods);
            checkRef();
            generic = Boolean.FALSE.toString();
        }

  

    在检查配置的时候,会启动 配置中心ConfigCenter,这个后续单独解析。

            在2.7 以上版本,dubbo的configCenter 可以与RegistryCenter 分离,分开存储。

             从业务上来说,配置 与 注册 是2个不同的功能,是可以分开的。

             从性能上来看,才有zk为注册中心的,将ConfigCenter分离出去,可以降低zk 集群的性能压力。

    2.  url参数组装

             在配置与发布直接,还有一大段冗长的代码,

              * 各种url参数的组装

              * 服务接口的wrap ,wrap后,进行方法的校验等处理

     3. 本地发布

         injvm 协议发布, 这个是网络发布的一个简化协议,主要是jvm里面的调用,本地调用的时候,会被filter包装,执行filter的过滤逻辑。

         主要步骤为: 构建invoker 的proxy,发布本地服务,  多注册中心,本地服务,只发布一次,多协议,会发布多次。

         在实际的场景里面,还没遇到过,既然是同1 个jvm的应用,直接注入服务实现类不是更好?

     4. url服务发布    

         主要过程为:1. 构建 InvokerProxy,2:构建 InvokeChain 3. DubboProctocol  生成exporter  4.启动nettyServer 

二、 服务发布

      1.  doExportUrls() 与 doExportUrlsFor1Protocol()

         2个方法共同实现服务发布。伪代码如下:

void doExportUrls{
     List registerList = loadRegistries() //加载注册中心列表,多注册中心
     for(configProtocol : configProtocolList){ //遍历协议列表, 多协议发布
          // 下面代码为doExportUrlsFor1Protocol 方法伪代码
         if( ! onlyRemote){ //可发布jvm
              injvm 协议发布服务
          }
         if(!onlyJVM){ //可发布remote
             for(registerUrl: registerList){ //遍历注册中心列表
               remote 协议&注册中心发布 
          }
        }
     }
 }                    

 从以上伪代码可以看出, 多协议,多注册中心情况下, 本地jvm发布,仅发布多协议,  remote发布,会发布协议与注册中心的笛卡尔积

  2. remote 发布:

Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString())); // 1
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //2
 
Exporter<?> exporter = protocol.export(wrapperInvoker); //3
exporters.add(exporter); //4

 以上4行代码,就是源码里面发布remote服务的代码,分行解释。

  1. 代码1,ref代表着接口的impl对象, 该行代码,主要是生成实际调用的Invoker, 实际为接口实现的proxy。 默认采用 javassist 实现。 (因历史原因 1.6版本javassist代理效率要高于jdk2倍以上,在1.7及以上版本,网上的测试结果来看,jdk的效率已经超越了javassist)

 2. 代码2,对Invoker 进行Delegate封装,主要是在invoker里面放入的metadata 。 具体用处没找到,可能是为后续的扩展或加入其它功能,预留的 

 3. 代码3, 改行功能发布服务, 因为protocol 进行过非常深的包装,这里面功能非常复杂。这个单独说。

 4. 代码4 ,这里比较简单,就是把该exporter,加入集合,可进行生命周期管理。exporter的下架等操作。

  

三、 protocol.export 方法

     接上面的代码3 : Exporter<?> exporter = protocol.export(wrapperInvoker); //3

   1. protocol 最开始为 protocol的adaptiveExtension ,是自动生成的。

       

   上图为adaptiveProtocol的代码栈内容,无法单行debug。  fitler 与listener 的顺序在这里不重要。

       调用adaptiveProtocol.export(),执行如下:

  •  根据传入的invoker的url ,protocol=register ,所以会获取RegisterProtocol, 因为Protocol扩展点有 ProtocolFilterWrapper与ProtocolListenerWrapper 两个wrap 扩展,所以会被包装2次,形成链。
  •  调用 adaptiveProtocol 调用export方法时,调用链如下: adaptive.export() ->ProtocolListenerWrapper.export() ->ProtocolFilterWrapper.export()->RegisterProtocol.export() 。
  •  因为是registerProtocol,在Listener与Filter里面,是没有执行任何动作,直接进入到RegisterProtocol的export() 方法的。

  2. 执行RegisterProtocol.export()

     上步执行时,会进入RegisterProtocol.export() 方法。

     该方法执行步骤如下:

  1.     协议转换,url从regiter protocol 转换到具体的发布协议url,如dubboProtocol,redisProtocol 等    ,才有默认的dubbo
  2.    调用发布协议,执行发布,生成exporter,将exporter包装成 ExporterChangeableWrapper,该类功能主要为 服务关闭unexport操作。
  3.    在2,完成服务发布后,调用RegistryFactory.adaptive() 动态获取register (注册中心),注册 服务url被ovrride 覆盖的通知监听
  4.    将2返回的ExporterChangeableWrapper 的exporter包装成 DestroyableExporter ,返回

    在步骤2里面,会通过RegistryFactory.adaptive() 动态获取register,例如:NacosRegistryFactory、ZookeeperRegistryFactory ,因为动态获取,所以RegisterProtocol 只有1个实现

 3. DubboProtocol的export 。

     RegisterProtocol 里面,会调用 protocol.export(),protocol 会被ProtocolListenerWrapper 与ProtocolFilterWrapper 包装,实际调用发生的处理过程如下:

     ProtocolFilterWrapper.export()->ProtocolListenerWrapper.export() ->RegisterProtocol.export()         

     a)  ProtocolFilterWrapper.export() 方法,调用 buildInvokerChain() 方法,获取Fitler的activeExtension,组装成FilterChain 后只执行 listener.export(filterChain);

     b) ProtocolListenerWrapper.export() 方法,调用DubboProtocol.export()方法, 生成 ListenerExporterWrapper 返回。ListenerExporterWrapper 可监听export、unexport事件。

   

    DubboProtocol.export 方法被 b) 步骤给预先调用, invoker 参数,为 a) 步骤的包装生成的filterChain .

    在DubboProtocol 里面,会启动NettyServer,打开端口监听,初始的消息handler 为dubboprotocol 内部的requestHandler。 

   打开端口监听,这里面的事情还很多,后续单独分析。

 四、总结

     服务发布,整体流程比较清晰,但是在debug的时候,因为dubbo 类的单一职责,导致太多的包装类、链,过程非常痛苦,一定要有耐心。

    RegisterProtocol 因为调用RegisterFactory, 所以只有1个实现,封装的也很好

    具体发布协议如DubboProtocol 、HttpProtocol ,里面封装了Server的打开,消息Handler 都是在协议内部生成,设计的很好。 

   Server 打开,ExchangeServer封装,内部handler 处理,又是一个长串的包装链,这个后续继续总结。

猜你喜欢

转载自www.cnblogs.com/keep-code/p/11061058.html
今日推荐