Dubbo 3.0 : 服务注册

2,099 阅读7分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

服务发现的作用主要范围包括向注册中心注册服务 , 以及服务的发现 (Feign 的负载均衡是在 Client 端做的 , 猜测Dubbo负载均衡也是)

Dubbo 提供多种注册中心 , 常见的有 Nacos , Zookeeper 和 Redis , 此篇也只看看3种

二 . 服务发现流程

2.1 服务发现的调用逻辑

// 服务发现的起点还是 BootStrap , 由 Listener 发起流程

C- DubboBootstrapApplicationListener # onContextRefreshedEvent
C- DubboBootstrap # start : 开始初始化
C- DubboBootstrap # exportServices : 注册 DubboService
C- ServiceConfig # export : 调用init 执行初始化 , 校验 config , 设置 serviceMetadata 元数据
C- ServiceConfig # doExport : 没太多逻辑 , 下层调用而已
C- ServiceConfig # doExportUrls : ServiceRepository 进行保存和下层调用
C- ServiceConfig # doExportUrlsFor1Protocol
C- ProtocolFilterWrapper # export
C- ProtocolListenerWrapper # export
C- RegistryProtocol # export : 发起注册逻辑
    
    
    

Zookeeper Service 详情

{
	"name": "dubbo-demo-annotation-provider",
	"id": "10.10.100.140:20880",
	"address": "10.10.100.140",
	"port": 20880,
	"sslPort": null,
	"payload": {
		"@class": "org.apache.dubbo.registry.zookeeper.ZookeeperInstance",
		"id": null,
		"name": "dubbo-demo-annotation-provider",
		"metadata": {
			"REGISTRY_CLUSTER": "registryConfig",
			"anyhost": "true",
			"application": "dubbo-demo-annotation-provider",
			"deprecated": "false",
			"dubbo": "2.0.2",
			"dubbo.endpoints": "[{\"port\":20880,\"protocol\":\"dubbo\"}]",
			"dubbo.metadata-service.url-params": "{\"version\":\"1.0.0\",\"dubbo\":\"2.0.2\",\"port\":\"20880\",\"protocol\":\"dubbo\"}",
			"dubbo.metadata.revision": "3c1be7b82505c5da23a5bc1cd1211417",
			"dubbo.metadata.storage-type": "local",
			"dynamic": "true",
			"generic": "false",
			"interface": "org.apache.dubbo.demo.DemoService",
			"metadata-type": "remote",
			"methods": "sayHello,sayHelloAsync",
			"pid": "21576",
			"release": "",
			"side": "provider",
			"timestamp": "1626856228483"
		}
	},
	"registrationTimeUTC": 1626856236245,
	"serviceType": "DYNAMIC",
	"uriSpec": null
}

Zookeeper 中数据情况

Dubbo-zookeeper-service.png

2.2 调用的起点

C- DubboBootstrap # start
C- DubboBootstrap # exportServices : start 逻辑中发起 export 处理

private void exportServices() {
    // Step 1 : configManager.getServices() 获取 Service 列表 -> PRO00001
    for (ServiceConfigBase sc : configManager.getServices()) {
    
        // 获取当前 ServiceConfig , 并且为其设置容器
        ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
        serviceConfig.setBootstrap(this);
        if (!serviceConfig.isRefreshed()) {
            // 需要刷新时对其进行刷新
            serviceConfig.refresh();
        }
        
        // 需要异步处理 -> export-async
        if (sc.shouldExportAsync()) {
        
            // 明显看到这里使用 ScheduledExecutorService + future 发起了异步调用
            ExecutorService executor = executorRepository.getExportReferExecutor();
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    if (!sc.isExported()) {
                        sc.export();
                        exportedServices.add(sc);
                    }
                } catch (Throwable t) {
                    logger.error("export async catch error : " + t.getMessage(), t);
                }
            }, executor);

            asyncExportingFutures.add(future);
        } else {
            // 这里逻辑都是一样的 , 没有导出过的进行导出 ,同时添加到对应集合中
            if (!sc.isExported()) {
                sc.export();
                exportedServices.add(sc);
            }
        }
    }
}


// PRO00001 : configManager.getServices 列表
<dubbo:service path="org.apache.dubbo.demo.DemoService" ref="org.apache.dubbo.demo.provider.DemoServiceImpl@60990e5c" generic="false" deprecated="false" dynamic="true" id="ServiceBean:org.apache.dubbo.demo.DemoService" />
    
  

补充 configManager

该方法从configsCache 中获取对应的参数 ,此处取得是 service

ConfigsCache.png

添加的流程 :

  • C- AbstractConfig # addIntoConfigManager -> org.apache.dubbo.config.spring.ServiceBean
  • C- ConfigManager # addConfig : 添加 Config , 用于前文循环时使用

可以看到 , 第一步时使用的就是 serverBean , 这个Bean 在扫描时创建 , 详见 -> Dubbo 3.0 : DubboService 的扫描

2.3 解析出 Service

中间没有太多东西 , 主要调用流程为 :

  • C- ServiceConfig # export :
  • C- ServiceConfig # doExport :
    • C- ServiceConfig # doExportUrls :
    • C- ServiceConfig # exported :
// 在此阶段 , 构建出 Registry URL , 用于 Zookeeper 注册
private void doExportUrls() {
    
    // PRO0001 : 
    ServiceRepository repository = ApplicationModel.getServiceRepository();
    // PRO0002 : 
    ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
    repository.registerProvider(
            getUniqueServiceName(),
            ref,
            serviceDescriptor,
            this,
            serviceMetadata
    );
	
    // PRO0003 : 
    List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

    // PRO0004 : 
    for (ProtocolConfig protocolConfig : protocols) {
        // org.apache.dubbo.demo.DemoService
        String pathKey = URL.buildKey(getContextPath(protocolConfig)
                .map(p -> p + "/" + path)
                .orElse(path), group, version);
        // 如果用户指定了路径,则再次注册服务,将其映射到路径
        repository.registerService(pathKey, interfaceClass);
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

// PRO0001 : ServiceRepository 结构
{
    "services": {
        "serviceName": "org.apache.dubbo.rpc.service.EchoService",
        "serviceName": "org.apache.dubbo.rpc.service.GenericService",
        "serviceName": "org.apache.dubbo.monitor.MetricsService",
        "serviceName": "org.apache.dubbo.monitor.MonitorService",
        "serviceName": "org.apache.dubbo.demo.DemoService",
        
    },
    "providers": {
        "org.apache.dubbo.demo.DemoService": "ProviderModel"
    },
    "providersWithoutGroup": "org.apache.dubbo.demo.DemoService:0.0.0"
}
    
    


// PRO0003 : 构建 RegistryURL 
service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=registryConfig&application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=28256&registry=zookeeper&timestamp=1626858537404

registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=registryConfig&application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=28256&registry=zookeeper&timestamp=1626858537404


2.4 执行主流程

其中主步骤分为 3 步 :

  • Step 1 : 属性准备
  • Step 2 : 获取 host port , 构建 url
  • Step 3 : 构建 Invoke , export 导出
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    String name = protocolConfig.getName();
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }

    // Step 1 : 属性准备
    Map<String, String> map = new HashMap<String, String>();\
    // String PROVIDER_SIDE = "provider";
    // String SIDE_KEY = "side";
    map.put(SIDE_KEY, PROVIDER_SIDE);
    
    // 此环节添加属性
    ServiceConfig.appendRuntimeParameters(map);
    AbstractConfig.appendParameters(map, getMetrics());
    AbstractConfig.appendParameters(map, getApplication());
    AbstractConfig.appendParameters(map, getModule());
    // remove 'default.' prefix for configs from ProviderConfig
    // appendParameters(map, provider, Constants.DEFAULT_KEY);
    AbstractConfig.appendParameters(map, provider);
    AbstractConfig.appendParameters(map, protocolConfig);
    AbstractConfig.appendParameters(map, this);
    
    MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
    if (metadataReportConfig != null && metadataReportConfig.isValid()) {
        map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
    }
    if (CollectionUtils.isNotEmpty(getMethods())) {
        //TODO Improve method config processing
        //... 此处如果 dubbo:method 进行了方法级处理 , 此处会深入处理 , 同样是添加 method 处理
    }

    if (ProtocolUtils.isGeneric(generic)) {
        map.put(GENERIC_KEY, generic);
        map.put(METHODS_KEY, ANY_VALUE);
    } else {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put(REVISION_KEY, revision);
        }

        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        // ["sayHello","sayHelloAsync"]
        if (methods.length == 0) {
            // 省略为空逻辑
        } else {
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }

    // 提供程序配置的令牌值用于将值分配给ServiceConfig#令牌
    if (ConfigUtils.isEmpty(token) && provider != null) {
        token = provider.getToken();
    }

    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {
            map.put(TOKEN_KEY, UUID.randomUUID().toString());
        } else {
            map.put(TOKEN_KEY, token);
        }
    }
    // 讲所有的 map 放入 元数据中
    serviceMetadata.getAttachments().putAll(map);

    // Step 2 : 获取 host port , 构建 url
    String host = findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = findConfigedPorts(protocolConfig, name, map);
    // PRO24001 -> url 
    URL url = new ServiceConfigURL(name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

    // 自定义Configurator以附加额外参数
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    String scope = url.getParameter(SCOPE_KEY);
    // don't export when none is configured
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

        // 如果配置不是远程的,则导出到本地
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            exportLocal(url);
        }
        // 如果配置不是本地的,则导出到远程
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            if (CollectionUtils.isNotEmpty(registryURLs)) {
                // PIC24002 -> registryURLs
                for (URL registryURL : registryURLs) {
                    //if protocol is only injvm ,not register
                    if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                        continue;
                    }
                    url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                    URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                    if (monitorUrl != null) {
                        url = url.putAttribute(MONITOR_KEY, monitorUrl);
                    }

                    // For providers, this is used to enable custom proxy to generate invoker
                    String proxy = url.getParameter(PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                    }

                    // Step 3 : 构建 Invoke ,  export 导出
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.putAttribute(EXPORT_KEY, url));
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                    // -> 2.5 Protocol 注册
                    Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            } else {
                if (MetadataService.class.getName().equals(url.getServiceInterface())) {
                    MetadataUtils.saveMetadataURL(url);
                }
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                exporters.add(exporter);
            }

            MetadataUtils.publishServiceDefinition(url);
        }
    }
    this.urls.add(url);
}

PRO24001 案例 : providerUrl

dubbo://192.168.181.2:20880/org.apache.dubbo.demo.DemoService
    ?anyhost=true
    &application=dubbo-demo-annotation-provider
    &bind.ip=192.168.181.2
    &bind.port=20880
    &deprecated=false
    &dubbo=2.0.2
    &dynamic=true
    &generic=false
    &interface=org.apache.dubbo.demo.DemoService
    &metadata-type=remote
    &methods=sayHello,sayHelloAsync
    &pid=17324
    &release=
    &side=provider
    &timestamp=1628690334012

PRO24002 案例 : registryUrl

service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService
?REGISTRY_CLUSTER=registryConfig
&application=dubbo-demo-annotation-provider
&dubbo=2.0.2
&pid=17324
&registry=zookeeper
&timestamp=1628690331733

PIC24002 image.png

2.5 传入代理类

在这一步构建了 url ,同时生成了 ServiceDiscoveryRegistry

C- RegistryProtocol
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {

    // service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService....
    URL registryUrl = getRegistryUrl(originInvoker);
    // dubbo://192.168.181.2:20880/org.apache.dubbo.demo.DemoService?anyhost=true....
    URL providerUrl = getProviderUrl(originInvoker);

    // PRO0003 : URL 参数
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    
    // 重写 URL
    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    //export invoker
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // Step 3 :  通过 Registry Factory 获取使用的 Registry , 例如 : ZookeeperRegistry -> 3.1 
    final Registry registry = getRegistry(registryUrl);
    // Step 4 :  构建注册对象 -> PRO0003
    final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

    // Step 5 :  注册发布服务
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) **{**
        // -> 2.5.2 Registry 的管理
        register(registry, registeredProviderUrl);
    }

    // 在提供程序模型上注册声明的url
    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);
}


// PRO0003 : URL 参数
provider://192.168.181.2:20880/org.apache.dubbo.demo.DemoService
?anyhost=true
&application=dubbo-demo-annotation-provider
&bind.ip=192.168.181.2
&bind.port=20880
&category=configurators
&check=false
&deprecated=false
&dubbo=2.0.2
&dynamic=true
&generic=false
&interface=org.apache.dubbo.demo.DemoService
&metadata-type=remote
&methods=sayHello,sayHelloAsync
&pid=17324
&release=
&side=provider
&timestamp=1628690334012

Registry 可以分为以下几个部分 :

  • 由 getRegistry 发起获取 Registry
  • createRegistry 发起创建逻辑
  • RegistryProtocol 调用 doRegister 执行 Register

三 . Registry 的创建

共用流程

// Step 1 : 获取 Registry
C- RegistryFactoryWrapper
public Registry getRegistry(URL url) {
	return new ListenerRegistryWrapper(registryFactory.getRegistry(url),
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(RegistryServiceListener.class)
                        .getActivateExtension(url, "registry.listeners")));
}

// Step 2 : 调用工厂创建 registry
C- AbstractRegistryFactory
public Registry getRegistry(URL url) {

    Registry defaultNopRegistry = getDefaultNopRegistryIfDestroyed();
    if (null != defaultNopRegistry) {
        return defaultNopRegistry;
    }

    url = URLBuilder.from(url)
            .setPath(RegistryService.class.getName())
            .addParameter(INTERFACE_KEY, RegistryService.class.getName())
            .removeParameters(EXPORT_KEY, REFER_KEY, TIMESTAMP_KEY)
            .build();
    String key = createRegistryCacheKey(url);
    // 上锁
    LOCK.lock();
    try {

        //..........
        
        // Map<String, Registry> REGISTRIES = new HashMap<>();
        Registry registry = REGISTRIES.get(key);
        if (registry != null) {
            return registry;
        }
        //通过 spi/ioc 的方式获取 Registry -> org.apache.dubbo.registry.client.ServiceDiscoveryRegistry
        registry = createRegistry(url);
        
        // 放入缓存
        REGISTRIES.put(key, registry);
        return registry;
    } finally {
        // Release the lock
        LOCK.unlock();
    }
}

3.1 创建 ServiceDiscoveryRegistry

// Step 3 : ServiceDiscoveryRegistryFactory 
protected Registry createRegistry(URL url) {
    if (UrlUtils.hasServiceDiscoveryRegistryProtocol(url)) {
        // 此处拿到的为 zookeeper
        String protocol = url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY);
        // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=registryConfig&application=dubbo-demo-annotation-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=2816
        url = url.setProtocol(protocol).removeParameter(REGISTRY_KEY);
    }
        
    return new ServiceDiscoveryRegistry(url);
}

public ServiceDiscoveryRegistry(URL registryURL) {
	this.registryURL = registryURL;
	this.serviceDiscovery = createServiceDiscovery(registryURL);
	this.writableMetadataService = WritableMetadataService.getDefaultExtension();
}

// 下次调用 , 
protected ServiceDiscovery createServiceDiscovery(URL registryURL) {
    ServiceDiscovery serviceDiscovery = getServiceDiscovery(registryURL);
    execute(() -> {
        // 初始化操作
        serviceDiscovery.initialize(registryURL.addParameter(INTERFACE_KEY, ServiceDiscovery.class.getName())
                    .removeParameter(REGISTRY_TYPE_KEY));
    });
    return serviceDiscovery;
}

static WritableMetadataService getDefaultExtension() {
	return getExtensionLoader(WritableMetadataService.class).getDefaultExtension();
}

3.2 创建 ZookeeperRegistry

Zookeeper 只是 Dubbo 支持的注册中心之一 , 以其为例看一下创建和使用流程 :

这里有一个核心点 , ZookeeperRegistry 的创建是在 , 它创建时会被包裹为一个 ListenerRegistryWrapper 对象

C- ZookeeperRegistryFactory
public Registry createRegistry(URL url) {
	return new ZookeeperRegistry(url, zookeeperTransporter);
}

// 创建 ZookeeperRegistry
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        String group = url.getGroup(DEFAULT_ROOT);
        if (!group.startsWith(PATH_SEPARATOR)) {
            group = PATH_SEPARATOR + group;
        }
        this.root = group;
        zkClient = zookeeperTransporter.connect(url);
        zkClient.addStateListener((state) -> {
            if (state == StateListener.RECONNECTED) {
                ZookeeperRegistry.this.fetchLatestAddresses();
            } else if (state == StateListener.NEW_SESSION_CREATED) {
                try {
                    ZookeeperRegistry.this.recover();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            } else if (state == StateListener.SESSION_LOST) {
              
            } else if (state == StateListener.SUSPENDED) {

            } else if (state == StateListener.CONNECTED) {

            }
        });
    }


C- ExtensionLoader 
private T createExtension(String name, boolean wrap) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null || unacceptableExceptions.contains(name)) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);


            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                    for (Class<?> wrapperClass : wrapperClassesList) {
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }

            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }    

补充 : ListenerRegistryWrapper 可以看到 , 这里 ListenerRegistryWrapper 中包含了一个 Registry 对象

public class ListenerRegistryWrapper implements Registry {
    private static final Logger logger = LoggerFactory.getLogger(ListenerRegistryWrapper.class);

    private final Registry registry;
    private final List<RegistryServiceListener> listeners;
    
}

四 . Registry 的调用

Registry 在 RegistryProtocol # register 进行注册 , 会调用父类 FailbackRegistry , 最终调用各种的实现类

FailbackRegistry 通用处理

// 此处以 FailbackRegistry 为例 
 public void register(URL url) {
        if (!acceptable(url)) {
            return;
        }
        super.register(url);
        removeFailedRegistered(url);
        removeFailedUnregistered(url);
        try {
            // Sending a registration request to the server side
            doRegister(url);
        } catch (Exception e) {
            // 省略部分异常处理逻辑
            // Record a failed registration request to a failed list, retry regularly
            addFailedRegistered(url);
        }
    }

System-AbstractRegistry.png

4.1 ServiceDiscoveryRegistry 注册

// Step 1 : 发起 Registry
C- RegistryProtocol
private void register(Registry registry, URL registeredProviderUrl) {
	registry.register(registeredProviderUrl);
}


C- ServiceDiscoveryRegistry
public final void register(URL url) {
	if (!shouldRegister(url)) { // Should Not Register
		return;
	}
	doRegister(url);
        //--> url = addRegistryClusterKey(url); 其中调用 addRegistryClusterKey
}

// Step 2 : 获取 RegistryClusterKey
private URL addRegistryClusterKey(URL url) {
        String registryCluster = serviceDiscovery.getUrl().getParameter(REGISTRY_CLUSTER_KEY);
        if (registryCluster != null && url.getParameter(REGISTRY_CLUSTER_KEY) == null) {
            url = url.addParameter(REGISTRY_CLUSTER_KEY, registryCluster);
        }
        // dubbo://192.168.181.2:20880/org.apache.dubbo.demo.DemoService?REGISTRY_CLUSTER=registryConfig&anyhost=true&application=dubbo-demo-annotation-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&metadata-type=remote&methods=sayHello,sayHelloAsync&pid=13200&release=&side=provider&timestamp=1626146555672
        return url;
}

// Step 3 : doRegister 进行注册
public void doRegister(URL url) {
    url = addRegistryClusterKey(url);
    // 其中主要是对 Map 进行操作
    // ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new ConcurrentSkipListMap<>(); 
    if (writableMetadataService.exportURL(url)) {
       
    } else {
      
    }
}

    

4.2 ZookeeperRegistry 注册

public void doRegister(URL url) {
    try {
        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);
    }
}

五 . 补充深入

5.1 服务注册器的选择

// Dubbo 提供了 Zookeeper , Nacos 等多个服务注册器 , 这里来看一下此处是如何选择的 , 此处以 Nacos 为例

// 环节一 : 判断是否支持 Nacos ExtensionLoader
C- DubboBootstrap
private boolean isUsedRegistryAsCenter(RegistryConfig registryConfig, Supplier<Boolean> usedRegistryAsCenter,
                                           String centerType,
                                           Class<?> extensionClass) {
	final boolean supported;

	Boolean configuredValue = usedRegistryAsCenter.get();
	if (configuredValue != null) { // If configured, take its value.
		supported = configuredValue.booleanValue();
	} else {                       // Or check the extension existence
		String protocol = registryConfig.getProtocol();
		supported = supportsExtension(extensionClass, protocol);
	}


	return supported;
}

C- DubboBootstrap
private boolean supportsExtension(Class<?> extensionClass, String name) {
	if (isNotEmpty(name)) {
		ExtensionLoader extensionLoader = getExtensionLoader(extensionClass);
		return extensionLoader.hasExtension(name);
	}
	return false;
}

// 环节二 : 获取 ExtensionLoader

Nacos 请求逻辑

// 调用逻辑
C- DubboBootstrapApplicationListener # onApplicationContextEvent 
C- DubboBootstrapApplicationListener # onContextRefreshedEvent
C- DubboBootstrap # start
C- DubboBootstrap  # exportServices
C- ServiceConfig # export + doExport + doExportUrlsFor1Protocol : 从配置中获取URL
C- RegistryProtocol # export
C- NacosNamingService # getAllInstances
C- NamingGrpcClientProxy # subscribe
C- NamingGrpcClientProxy # requestToServer

Zookeeper 请求逻辑

// 调用逻辑
C- CuratorZookeeperClient.<init>
C- CuratorZookeeperTransporter.createZookeeperClient
C- AbstractZookeeperTransporter.connect
C- ZookeeperTransporter$Adaptive.connect
C- ZookeeperDynamicConfiguration.<init>
C- ZookeeperDynamicConfigurationFactory.createDynamicConfiguration
C- AbstractDynamicConfigurationFactory.getDynamicConfiguration
C- DynamicConfiguration.getDynamicConfiguration
C- DubboBootstrap.prepareEnvironment
C- DubboBootstrap.startConfigCenter
C- DubboBootstrap.initialize
C- DubboBootstrap.start
C- DubboBootstrapApplicationListener.onContextRefreshedEvent
C- DubboBootstrapApplicationListener.onApplicationContextEvent

总结 :

ServiceDiscoveryRegistry 这一块没深入太多 , 后面有时间再看看 , 下面来看一下完整的流程图

Dubbo-Registry-Service.png