Dubbo源码之服务注册

Dubbo中的服务都会通过ServiceConfig(Spring容器为ServiceBean)对象的export()方法进行服务注册。

1. ServiceConfig.export()

export为服务注册的入口,内部调用了doExport()方法和exported()方法。

    public synchronized void export() {
        if (shouldDelay()) {
            // 延迟注册
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            // 注册
            doExport();
        }
        //发布服务注册事件 ServiceConfigExportedEvent
        exported();
    }

2. ServiceConfig.doExport()

doExport方法很简单,标记注册状态exportedtrue,并设置默认path为接口名称。

    protected synchronized void doExport() {
        if (unexported) {
            throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
        }
        if (exported) {
            return;
        }
        exported = true;

        if (StringUtils.isEmpty(path)) {
            // 如果没有设置path,默认为 interfaceName
            // interfaceName: org.finalframework.dubbo.service.HelloService
            path = interfaceName;
        }
        doExportUrls();
    }

3. ServiceConfig.doExportUrls()

    private void doExportUrls() {
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );

        // 解析注册地址 <dubbo:registry address="zookeeper://127.0.0.1:2181" protocol="zookeeper" port="2181" />
        // 格式: registry://{host}:{port}/org.apache.dubbo.registry.RegistryService?
        // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&pid=16114&registry=zookeeper&release=2.7.8&timestamp=1598503117736
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

        //遍历协议进行服务注册
        // <dubbo:protocol name="dubbo" port="20880" />
        for (ProtocolConfig protocolConfig : protocols) {
            // pathKey = [{group}/]{interfaceServiceName}[:{version}]
            // org.finalframework.dubbo.service.HelloService
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            // In case user specified path, register service one more time to map it to path.
            repository.registerService(pathKey, interfaceClass);
            // TODO, uncomment this line once service key is unified
            serviceMetadata.setServiceKey(pathKey);
            //进行服务注册
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

4. ConfigValidationUtils.loadRegistries(AbstractInterfaceConfig, boolean)


    public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
        // check && override if necessary
        List<URL> registryList = new ArrayList<URL>();
        // <dubbo:application hostname="MAC.local" name="final-dubbo-provider" />
        ApplicationConfig application = interfaceConfig.getApplication();
        // <dubbo:registry address="zookeeper://127.0.0.1:2181" protocol="zookeeper" port="2181" />
        List<RegistryConfig> registries = interfaceConfig.getRegistries();
        if (CollectionUtils.isNotEmpty(registries)) {
            for (RegistryConfig config : registries) {
                String address = config.getAddress();
                if (StringUtils.isEmpty(address)) {
                    address = ANYHOST_VALUE;
                }
                if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                    Map<String, String> map = new HashMap<String, String>();
                    AbstractConfig.appendParameters(map, application);
                    AbstractConfig.appendParameters(map, config);
                    map.put(PATH_KEY, RegistryService.class.getName());
                    AbstractInterfaceConfig.appendRuntimeParameters(map);
                    if (!map.containsKey(PROTOCOL_KEY)) {
                        map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
                    }
                    List<URL> urls = UrlUtils.parseURLs(address, map);

                    for (URL url : urls) {

                        url = URLBuilder.from(url)
                                .addParameter(REGISTRY_KEY, url.getProtocol())
                                .setProtocol(extractRegistryType(url))
                                .build();
                        if ((provider && url.getParameter(REGISTER_KEY, true))
                                || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                            registryList.add(url);
                        }
                    }
                }
            }
        }
        return registryList;
    }

5. ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig , List )

  private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
      ...
      // export service
      String host = findConfigedHosts(protocolConfig, registryURLs, map);
      Integer port = findConfigedPorts(protocolConfig, name, map);
      // dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=16114&release=2.7.8&side=provider&timestamp=1598504045682
      URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

      ...

      //获取Scope,一般为 null
      String scope = url.getParameter(SCOPE_KEY);
      // don't export when none is configured
      if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

          // export to local if the config is not remote (export to remote only when config is remote)
          if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
              //本地注册
              exportLocal(url);
          }
          // export to remote if the config is not local (export to local only when config is local)
          if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
              // 远程注册
              if (CollectionUtils.isNotEmpty(registryURLs)) {
                  for (URL registryURL : registryURLs) {
                      ...
                      // JavassistProxyFactory.getInvoker(proxy,Class,URL)
                      Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                      DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                      // RegistryProtocol.export(Invoker)
                      Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                      exporters.add(exporter);
                  }
              } else {
                  ...
                  // JavassistProxyFactory.getInvoker(proxy,Class,URL)
                  Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                  DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                  // RegistryProtocol.export(Invoker)
                  Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                  exporters.add(exporter);
              }
              
          }
      }
      ...
  }

6. ServiceConfig.exportLocal(URL)

exportLocal(URL)方法

private void exportLocal(URL url) {
    // url: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=25489&release=2.7.8&side=provider&timestamp=1598529167646
    // local: injvm://127.0.0.1/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=17480&release=2.7.8&side=provider&timestamp=1598504670606
    URL local = URLBuilder.from(url)
            .setProtocol(LOCAL_PROTOCOL)
            .setHost(LOCALHOST_VALUE)
            .setPort(0)
            .build();
    // PROTOCOL: org.apache.dubbo.rpc.Protocol$Adaptive
    // jad org.apache.dubbo.rpc.Protocol$Adaptive
    // exporter: ListenerExporterWrapper
    // exporter.exporter: InJvmExporter
    Exporter<?> exporter = PROTOCOL.export(
            PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
    exporters.add(exporter);
    logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}

7. JavassistProxyFactory.getInvoker(proxy,Class,URL)

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        // 创建动态代理
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

8.RegistryProtocol.export(Invoker)

  • export(Invoker)

    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //originInvoker.url: registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&export=dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889&pid=26061&registry=zookeeper&release=2.7.8&timestamp=1598530524880
        
        // registryUrl: zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&export=dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889&pid=26061&release=2.7.8&timestamp=1598530524880
        URL registryUrl = getRegistryUrl(originInvoker);
        // url to export locally
        // providerUrl: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889
        URL providerUrl = getProviderUrl(originInvoker);

        // Subscribe the override data
        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
        //  the same service. Because the subscribed is cached key with the name of the service, it causes the
        //  subscription information to cover.
        // overrideSubscribeUrl: provider://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

        // providerUrl: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889
        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        //export invoker
        // 本地注册
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // url to registry
        //
        final Registry registry = getRegistry(originInvoker);
        // registeredProviderUrl: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889
        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

        // decide if we need to delay publish
        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
        if (register) {
            // 注册
            register(registryUrl, registeredProviderUrl);
        }

        // register stated url on provider model
        registerStatedUrl(registryUrl, registeredProviderUrl, register);


        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);

        // Deprecated! Subscribe to override rules in 2.6.x or before.
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

        notifyExport(exporter);
        //Ensure that a new exporter instance is returned every time export
        return new DestroyableExporter<>(exporter);
    }
  • getRegistry(Invoker)
    protected Registry getRegistry(final Invoker<?> originInvoker) {
        // registryUrl: zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&export=dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider&timestamp=1598530524889&pid=26061&release=2.7.8&timestamp=1598530524880
        URL registryUrl = getRegistryUrl(originInvoker);
        // registryFactory: RegistryFactory$Adaptive
        return registryFactory.getRegistry(registryUrl);
    }
  • register(URL,URL)
    private void register(URL registryUrl, URL registeredProviderUrl) {
        // registryFactory: RegistryFactory$Adaptive
        // jad org.apache.dubbo.registry.RegistryFactory$Adaptive
        // registry: ListenerRegistryWrapper->ZookeeperRegistry
        Registry registry = registryFactory.getRegistry(registryUrl);
        registry.register(registeredProviderUrl);
    }

9. ZookeeperRegistry.register(URL)

  • FailbackRegistry#register(URL)

    @Override
    public void register(URL url) {
        if (!acceptable(url)) {
            logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type.");
            return;
        }
        super.register(url);
        removeFailedRegistered(url);
        removeFailedUnregistered(url);
        try {
            // Sending a registration request to the server side
            // ZookeeperRegistry.doRegister(URL)
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && !CONSUMER_PROTOCOL.equals(url.getProtocol());
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // Record a failed registration request to a failed list, retry regularly
            addFailedRegistered(url);
        }
    }
  • doRegister(URL)
    @Override
    public void doRegister(URL url) {
        // url: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=25489&release=2.7.8&side=provider&timestamp=1598529167646
        try {
            // zkClient: CuratorZookeeperClient
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
        } catch (Throwable e) {
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

10. CuratorZookeeperClient.create(String path, boolean ephemeral)

    public void create(String path, boolean ephemeral) {
        // path: /dubbo/org.finalframework.dubbo.service.HelloService/providers/dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=25489&release=2.7.8&side=provider&timestamp=1598529167646
        // ephemeral: true
        if (!ephemeral) {
            if(persistentExistNodePath.contains(path)){
                return;
            }
            if (checkExists(path)) {
                persistentExistNodePath.add(path);
                return;
            }
        }
        int i = path.lastIndexOf('/');
        if (i > 0) {
            create(path.substring(0, i), false);
        }
        if (ephemeral) {
            // 创建临时节点
            createEphemeral(path);
        } else {
            // 创建永久节点
            createPersistent(path);
            persistentExistNodePath.add(path);
        }
    }

11. ServiceConfig.exported()

exported()方法在服务注册完成后调用,用于分发ServiceConfigExportedEvent事件。

    public void exported() {
        // dispatch a ServiceConfigExportedEvent since 2.7.4
        dispatch(new ServiceConfigExportedEvent(this));
    }

Spring容器中时,ServiceBean重写了exported()方法,并调用了publishExportEvent()方法进行``事件的发布。

    @Override
    public void exported() {
        super.exported();
        // Publish ServiceBeanExportedEvent
        publishExportEvent();
    }

    /**
     * @since 2.6.5
     */
    private void publishExportEvent() {
        ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
        applicationEventPublisher.publishEvent(exportEvent);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345