Skip to content

服务注册与发现 ​

深入理解Dubbo服务注册与发现的实现机制,掌握服务暴露和引用的完整流程,理解注册中心的作用,掌握服务发现、服务路由的实现原理。

📋 服务注册发现概览 ​

服务注册与发现是微服务架构的核心功能,Dubbo通过注册中心实现服务的注册和发现。

🎯 一、服务暴露 ​

1.1 服务暴露流程 ​

1.2 ServiceConfig.export ​

ServiceConfig.export是服务暴露的入口:

java
public synchronized void export() {
    if (provider != null) {
        if (export == null) {
            export = provider.getExport();
        }
        if (delay == null) {
            delay = provider.getDelay();
        }
    }
    if (export != null && !export) {
        return;
    }
    
    if (delay != null && delay > 0) {
        delayExportExecutor.schedule(() -> {
            doExport();
        }, delay, TimeUnit.MILLISECONDS);
    } else {
        doExport();
    }
}

doExport方法:

java
protected synchronized void doExport() {
    if (unexported) {
        throw new IllegalStateException("Already unexported!");
    }
    if (exported) {
        return;
    }
    exported = true;
    
    if (interfaceName == null || interfaceName.length() == 0) {
        throw new IllegalStateException("interface not allow null!");
    }
    
    // 检查配置
    checkDefault();
    // 延迟暴露
    if (delay != null && delay > 0) {
        delayExportExecutor.schedule(() -> {
            doExportUrls();
        }, delay, TimeUnit.MILLISECONDS);
    } else {
        doExportUrls();
    }
}

1.3 注册到注册中心 ​

RegistryProtocol.export:

java
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    URL registryUrl = getRegistryUrl(originInvoker);
    URL providerUrl = getProviderUrl(originInvoker);
    
    // 注册到注册中心
    final Registry registry = getRegistry(originInvoker);
    final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
    registry.register(registedProviderUrl);
    
    // 暴露服务
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
    
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(
            originInvoker, registryUrl, registedProviderUrl);
    
    return new DestroyableExporter<>(exporter, () -> {
        try {
            providerInvokerWrapper.setReg(false);
            registry.unregister(registedProviderUrl);
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
    });
}

🎯 二、服务引用 ​

2.1 服务引用流程 ​

2.2 ReferenceConfig.get ​

ReferenceConfig.get是服务引用的入口:

java
public synchronized T get() {
    if (destroyed) {
        throw new IllegalStateException("Already destroyed!");
    }
    if (ref == null) {
        init();
    }
    return ref;
}

init方法:

java
private void init() {
    // 检查配置
    checkDefault();
    // 创建代理
    ref = createProxy(map);
}

2.3 从注册中心订阅 ​

RegistryProtocol.refer:

java
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    url = url.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
            .removeParameter(REGISTRY_KEY);
    Registry registry = registryFactory.getRegistry(url);
    
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }
    
    // 获取组和版本
    String group = url.getParameter(GROUP_KEY);
    String version = url.getParameter(VERSION_KEY);
    
    // 创建目录
    return doRefer(cluster, registry, type, url, group, version);
}

doRefer方法:

java
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, 
        Class<T> type, URL url, String group, String version) {
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    
    // 订阅服务
    URL subscribeUrl = new URL(CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 
            0, type.getName(), url.getParameters());
    if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
        directory.setRegisteredConsumerUrl(subscribeUrl);
        registry.register(subscribeUrl);
    }
    
    directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY, 
            PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
    
    // 构建Invoker
    Invoker<T> invoker = cluster.join(directory);
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}

🎯 三、注册中心 ​

3.1 注册中心接口 ​

Registry定义了注册中心的核心功能:

java
public interface Registry extends Node {
    void register(URL url);
    void unregister(URL url);
    void subscribe(URL url, NotifyListener listener);
    void unsubscribe(URL url, NotifyListener listener);
    List<URL> lookup(URL url);
}

3.2 Zookeeper注册中心 ​

ZookeeperRegistry.register:

java
@Override
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);
    }
}

3.3 Nacos注册中心 ​

NacosRegistry.register:

java
@Override
public void doRegister(URL url) {
    String serviceName = getServiceName(url);
    Instance instance = createInstance(url);
    try {
        namingService.registerInstance(serviceName, instance);
    } catch (NacosException e) {
        throw new RpcException("Failed to register " + url + " to nacos, cause: " + 
                e.getMessage(), e);
    }
}

🎯 四、服务发现 ​

4.1 服务列表更新 ​

当注册中心的服务列表发生变化时,会通知Directory更新:

RegistryDirectory.notify:

java
@Override
public synchronized void notify(List<URL> urls) {
    Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)
            .filter(this::isValidCategory)
            .filter(this::isNotCompatibleFor26x)
            .collect(Collectors.groupingBy(url -> {
                if (UrlUtils.isConfigurator(url)) {
                    return CONFIGURATORS_CATEGORY;
                } else if (UrlUtils.isRoute(url)) {
                    return ROUTERS_CATEGORY;
                } else if (UrlUtils.isProvider(url)) {
                    return PROVIDERS_CATEGORY;
                }
                return "";
            }));
    
    List<URL> configuratorUrls = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, 
            Collections.emptyList());
    this.configurators = Configurator.toConfigurators(configuratorUrls).orElse(
            this.configurators);
    
    List<URL> routerUrls = categoryUrls.getOrDefault(ROUTERS_CATEGORY, 
            Collections.emptyList());
    toRouters(routerUrls).ifPresent(this::addRouters);
    
    List<URL> providerUrls = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, 
            Collections.emptyList());
    refreshOverrideAndInvoker(providerUrls);
}

4.2 Invoker列表刷新 ​

refreshInvoker方法:

java
private void refreshInvoker(List<URL> invokerUrls) {
    if (invokerUrls != null && invokerUrls.size() == 1 
            && invokerUrls.get(0) != null 
            && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
        this.forbidden = true;
        this.invokers = Collections.emptyList();
        routerChain.setInvokers(this.invokers);
        destroyAllInvokers();
    } else {
        this.forbidden = false;
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
        if (invokerUrls == null || invokerUrls.isEmpty()) {
            return;
        }
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
        this.urlInvokerMap = newUrlInvokerMap;
        this.invokers = new ArrayList<>(newUrlInvokerMap.values());
        routerChain.setInvokers(this.invokers);
        destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);
    }
}

📖 学习资源 ​

视频教程 ​

书籍推荐 ​

  • 《深度剖析Apache Dubbo核心技术内幕》 - 华仔
  • 《分布式服务框架原理与实践》 - 李林锋

网站资源 ​

✅ 学习检查清单 ​

  • [ ] 已理解服务暴露流程
  • [ ] 已理解服务引用流程
  • [ ] 已理解注册中心的作用
  • [ ] 已理解服务发现机制
  • [ ] 已理解服务列表更新机制
  • [ ] 已绘制服务注册发现流程图
  • [ ] 已总结核心知识点

上一章节:RPC原理深度解析
下一章节:负载均衡机制
返回目录:Dubbo源码深度解析