Dubbo源码---服务导出

226 阅读23分钟

Dubbo源码---服务导出

Dubbo服务导出在ServiceConfig中export方法,整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装 URL。第二部分是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。第三部分是向注册中心注册服务,用于服务发现。本篇文章将会对这三个部分代码进行详细的分析。

前置工作

实例化DubboBootstrap

public static DubboBootstrap getInstance() {
    if (instance == null) {
        synchronized (DubboBootstrap.class) {
            if (instance == null) {
                instance = new DubboBootstrap();
            }
        }
    }
    return instance;
}
​
private DubboBootstrap() {
    // 获取ConfigManager
    configManager = ApplicationModel.getConfigManager();
    // 获取Environment
    environment = ApplicationModel.getEnvironment();
​
    // 注册DubboShutdownHook
    DubboShutdownHook.getDubboShutdownHook().register();
    ShutdownHookCallbacks.INSTANCE.addCallback(new ShutdownHookCallback() {
        @Override
        public void callback() throws Throwable {
            DubboBootstrap.this.destroy();
        }
    });
}

DubboBootstrap在实例化阶段做了三件事,第一件事是获取ConfigManager,第二件事是获取Enviroment,第三件事注册DubboShutdownHook,当Dubbo服务关闭时起到清理作用。

初始化DubboBootstrap

public void initialize() {
    if (!initialized.compareAndSet(false, true)) {
        return;
    }
    // 初始化框架扩展
    ApplicationModel.initFrameworkExts();
    // 启动配置中心
    startConfigCenter();
    // 加载远程调用配置
    loadRemoteConfigs();
    // 校验通用配置
    checkGlobalConfigs();
    // @since 2.7.8
    // 启动元数据中心
    startMetadataCenter();
    // 初始化元数据服务
    initMetadataService();
    // 初始化元数据服务导出
    initMetadataServiceExports();
    // 初始化事件监听器
    initEventListener();
    if (logger.isInfoEnabled()) {
        logger.info(NAME + " has been initialized!");
    }
}

初始化FramewrokExt

public static void initFrameworkExts() {
    // 此处为获取扩展类实例,即从ExtensionLoader中获取其实例
    Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
    for (FrameworkExt ext : exts) {
        // 初始化FrameworkExt
        ext.initialize();
    }
}

通过Dubbo SPI获取FrameworkExt的扩展类,并进行初始化。有ConfigManager、Environment、ServiceRepository,其中ConfigManager、ServiceRepository执行的是默认的空方法,关注下Environment的初始化。

public void initialize() throws IllegalStateException {
    // 获取ConfigManager
    ConfigManager configManager = ApplicationModel.getConfigManager();
    // 获取默认的配置中心
    Optional<Collection<ConfigCenterConfig>> defaultConfigs = configManager.getDefaultConfigCenter();
    // 遍历配置中心,并设置外部配置及应用外部配置
    defaultConfigs.ifPresent(configs -> {
        for (ConfigCenterConfig config : configs) {
            this.setExternalConfigMap(config.getExternalConfiguration());
            this.setAppExternalConfigMap(config.getAppExternalConfiguration());
        }
    });
    this.externalConfiguration.setProperties(externalConfigurationMap);
    this.appExternalConfiguration.setProperties(appExternalConfigurationMap);
}

Environment中的initialize方法,主要是获取配置中心,从这里可以看出,一个Dubbo应用,是可以同时存在多个配置中心的,并从配置中心的配置中获取参数设置到Environment中。

启动ConfigCenter


private void startConfigCenter() {
​
    // 如果未在environment及ConfigManager设置配置中心,且ConfigManager中添加了RegistryConfig,则将ConfigManager中的Registry设置为配置中心
    useRegistryAsConfigCenterIfNecessary();
​
    // 获取配置中心
    Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();
​
    // 校验配置中心
    if (CollectionUtils.isEmpty(configCenters)) {
        ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();
        // 刷新ConfigCenterConfig
        configCenterConfig.refresh();
        if (configCenterConfig.isValid()) {
            // 添加配置中心
            configManager.addConfigCenter(configCenterConfig);
            configCenters = configManager.getConfigCenters();
        }
    } else {
        for (ConfigCenterConfig configCenterConfig : configCenters) {
            // 刷新配置中心
            configCenterConfig.refresh();
            // 校验配置中心配置
            ConfigValidationUtils.validateConfigCenterConfig(configCenterConfig);
        }
    }
​
    if (CollectionUtils.isNotEmpty(configCenters)) {
        // 多配置中心管理类的实例
        CompositeDynamicConfiguration compositeDynamicConfiguration = new CompositeDynamicConfiguration();
        for (ConfigCenterConfig configCenter : configCenters) {
            // 将配置中心添加到多配置中心管理器
            compositeDynamicConfiguration.addConfiguration(prepareEnvironment(configCenter));
        }
        environment.setDynamicConfiguration(compositeDynamicConfiguration);
    }
    // 刷新所有配置
    configManager.refreshAll();
}

配置中心的启动分为了配置中心获取、校验配置中心、刷新配置中心、多配置中心管理、刷新ConfigManager中其中config配置。我们首先来看下useRegistryAsConfigCenterIfNecessary方法。

private void useRegistryAsConfigCenterIfNecessary() {
​
    // 我们通过DynamicConfiguration的加载状态来判断ConfigCenter是否已经启动
    if (environment.getDynamicConfiguration().isPresent()) {
        return;
    }
    // ConfigManager中的configCenters为空
    if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) {
        return;
    }
    // 添加配置中心
    configManager
            // 获取默认的Restry
            .getDefaultRegistries()
            .stream()
            // 过滤出使用Restry作为ConfigCenter的配置
            .filter(this::isUsedRegistryAsConfigCenter)
            // 将Registry转换成ConfigCenter
            .map(this::registryAsConfigCenter)
            // 最终的结果添加到ConfigManager的configCenters
            .forEach(configManager::addConfigCenter);
}

useRegistryAsConfigCenterIfNecessary方法很简单,就是判断是否需要把Registry设置为ConfigCenter,如果需要,则将Registr转换成ConfigCenter。回到这里继续往下看,需要判断useRegistryAsConfigCenterIfNecessary方法是否成功设置configCenter,如果没有,则需要创建一个并刷新。再来看下refresh方法:

public void refresh() {
    Environment env = ApplicationModel.getEnvironment();
    try {
        CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
        // 循环方法,获取覆盖值并将新值设置回方法
        Method[] methods = getClass().getMethods();
        for (Method method : methods) {
            if (MethodUtils.isSetter(method)) {
                try {
                    String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                    // 调用 isTypeMatch() 是为了避免重复和错误更新,例如,我们在 ReferenceConfig 中有两个“setGeneric”方法。
                    if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                        method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                    }
                } catch (NoSuchMethodException e) {
                    logger.info("Failed to override the property " + method.getName() + " in " +
                            this.getClass().getSimpleName() +
                            ", please make sure every property has getter/setter method provided.");
                }
            } else if (isParametersSetter(method)) {
                String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                if (StringUtils.isNotEmpty(value)) {
                    Map<String, String> map = invokeGetParameters(getClass(), this);
                    map = map == null ? new HashMap<>() : map;
                    map.putAll(convert(StringUtils.parseParameters(value), ""));
                    invokeSetParameters(getClass(), this, map);
                }
            }
        }
    } catch (Exception e) {
        logger.error("Failed to override ", e);
    }
}

从refresh这个方法可以看到,其主要逻辑是通过Environment获取配置文件信息,然后通过反射方式进行属性的setter注入。继续回到这里往下看是处理多配置中心以及刷新其余的配置。

加载RemoteConfig

private void loadRemoteConfigs() {
    // 将registry ids转换成RegistryConfig
    List<RegistryConfig> tmpRegistries = new ArrayList<>();
    Set<String> registryIds = configManager.getRegistryIds();
    registryIds.forEach(id -> {
        if (tmpRegistries.stream().noneMatch(reg -> reg.getId().equals(id))) {
            tmpRegistries.add(configManager.getRegistry(id).orElseGet(() -> {
                RegistryConfig registryConfig = new RegistryConfig();
                registryConfig.setId(id);
                // 刷新注册配置
                registryConfig.refresh();
                return registryConfig;
            }));
        }
    });
​
    // 添加注册配置(RegistryConfig)
    configManager.addRegistries(tmpRegistries);
​
    // 将protocol ids转换成ProtocolConfig
    List<ProtocolConfig> tmpProtocols = new ArrayList<>();
    Set<String> protocolIds = configManager.getProtocolIds();
    protocolIds.forEach(id -> {
        if (tmpProtocols.stream().noneMatch(prot -> prot.getId().equals(id))) {
            tmpProtocols.add(configManager.getProtocol(id).orElseGet(() -> {
                ProtocolConfig protocolConfig = new ProtocolConfig();
                protocolConfig.setId(id);
                // protocolConfig刷新
                protocolConfig.refresh();
                return protocolConfig;
            }));
        }
    });
    // 添加协议配置(ProtocolConfig)
    configManager.addProtocols(tmpProtocols);
}

加载RemoteConfig的按流程看下来,获取ConfigManager中的protocoIds、registryIds,并将他们转换成ProtocolConfig及RegistryConfig并刷新,最后将它们交给ConfigManager管理。

校验通用配置


通用配置的代码比较简单,流程化的校验,下面简单的把代码贴出来,不做过多的分析。

private void checkGlobalConfigs() {
    // 校验ApplicationConfig(应用配置)
    ConfigValidationUtils.validateApplicationConfig(getApplication());
​
    // 校验MetadataReportConfig(元数据导出配置)
    Collection<MetadataReportConfig> metadatas = configManager.getMetadataConfigs();
    if (CollectionUtils.isEmpty(metadatas)) {
        MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
        // 刷新
        metadataReportConfig.refresh();
        if (metadataReportConfig.isValid()) {
            configManager.addMetadataReport(metadataReportConfig);
            metadatas = configManager.getMetadataConfigs();
        }
    }
    if (CollectionUtils.isNotEmpty(metadatas)) {
        for (MetadataReportConfig metadataReportConfig : metadatas) {
            // 刷新
            metadataReportConfig.refresh();
            // 校验
            ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
        }
    }
​
    // 校验ProviderConfig(提供者配置)
    Collection<ProviderConfig> providers = configManager.getProviders();
    if (CollectionUtils.isEmpty(providers)) {
        configManager.getDefaultProvider().orElseGet(() -> {
            ProviderConfig providerConfig = new ProviderConfig();
            configManager.addProvider(providerConfig);
            // 刷新
            providerConfig.refresh();
            return providerConfig;
        });
    }
    for (ProviderConfig providerConfig : configManager.getProviders()) {
        ConfigValidationUtils.validateProviderConfig(providerConfig);
    }
    // 校验ConsumerConfig(消费者配置)
    Collection<ConsumerConfig> consumers = configManager.getConsumers();
    if (CollectionUtils.isEmpty(consumers)) {
        configManager.getDefaultConsumer().orElseGet(() -> {
            ConsumerConfig consumerConfig = new ConsumerConfig();
            configManager.addConsumer(consumerConfig);
            consumerConfig.refresh();
            return consumerConfig;
        });
    }
    for (ConsumerConfig consumerConfig : configManager.getConsumers()) {
        ConfigValidationUtils.validateConsumerConfig(consumerConfig);
    }
​
    // 校验MonitoryConfig(监控配置)
    ConfigValidationUtils.validateMonitorConfig(getMonitor());
    // 校验MetricsConfig
    ConfigValidationUtils.validateMetricsConfig(getMetrics());
    // 校验ModuleConfig
    ConfigValidationUtils.validateModuleConfig(getModule());
    // 校验SslConfig
    ConfigValidationUtils.validateSslConfig(getSsl());
}

流程化的校验,逻辑很简单,这里不做赘述。

检查ApplciationConfig
public static void validateApplicationConfig(ApplicationConfig config) {
    if (config == null) {
        return;
    }

    // <dubbo:application name="">校验name是否为null
    if (!config.isValid()) {
        throw new IllegalStateException("No application config found or it's not a valid config! " +
                "Please add <dubbo:application name=\"...\" /> to your spring config.");
    }

    // 在系统配置中设置dubbo service关闭等待事件
    String wait = ConfigUtils.getProperty(SHUTDOWN_WAIT_KEY);
    if (wait != null && wait.trim().length() > 0) {
        System.setProperty(SHUTDOWN_WAIT_KEY, wait.trim());
    } else {
        wait = ConfigUtils.getProperty(SHUTDOWN_WAIT_SECONDS_KEY);
        if (wait != null && wait.trim().length() > 0) {
            System.setProperty(SHUTDOWN_WAIT_SECONDS_KEY, wait.trim());
        }
    }

    // dubbo服务命名规则检查
    checkName(NAME, config.getName());
    // 检查应用程序所有者
    checkMultiName(OWNER, config.getOwner());
    // 检查应用程序的组织(BU)
    checkName(ORGANIZATION, config.getOrganization());
    // 检查架构层
    checkName(ARCHITECTURE, config.getArchitecture());
    // 检查环境
    checkName(ENVIRONMENT, config.getEnvironment());
    // 检查自定义参数
    checkParameterName(config.getParameters());
}
检查MetadataReportConfig
public static void validateMetadataConfig(MetadataReportConfig metadataReportConfig) {
    if (metadataReportConfig == null) {
        return;
    }
}
检查ProviderConfig
public static void validateProviderConfig(ProviderConfig config) {
    // 检查上下文路径
    checkPathName(CONTEXTPATH_KEY, config.getContextpath());
    // 检查线程池
    checkExtension(ThreadPool.class, THREADPOOL_KEY, config.getThreadpool());
    // 检查telnet
    checkMultiExtension(TelnetHandler.class, TELNET, config.getTelnet());
    // 检查状态检查
    checkMultiExtension(StatusChecker.class, STATUS_KEY, config.getStatus());
    // 检查Transport
    checkExtension(Transporter.class, TRANSPORTER_KEY, config.getTransporter());
    // 检查Exchange
    checkExtension(Exchanger.class, EXCHANGER_KEY, config.getExchanger());
}
检查ConsumerConfig
public static void validateConsumerConfig(ConsumerConfig config) {
    if (config == null) {
        return;
    }
}
检查MonitoryConfig
public static void validateMonitorConfig(MonitorConfig config) {
    if (config != null) {
        if (!config.isValid()) {
            logger.info("There's no valid monitor config found, if you want to open monitor statistics for Dubbo, " +
                    "please make sure your monitor is configured properly.");
        }

        checkParameterName(config.getParameters());
    }
}
检查MetricsConfig
public static void validateMetricsConfig(MetricsConfig metricsConfig) {
    if (metricsConfig == null) {
        return;
    }
}
检查ModuleConfig
public static void validateModuleConfig(ModuleConfig config) {
    if (config != null) {
        // 校验Module名称
        checkName(NAME, config.getName());
        // 校验Module所有者
        checkName(OWNER, config.getOwner());
        // 校验Module organization
        checkName(ORGANIZATION, config.getOrganization());
    }
}
检查SslConfig
public static void validateSslConfig(SslConfig sslConfig) {
    if (sslConfig == null) {
        return;
    }
}

在校验通用配置这个模块中,我们发现有很多配置的refresh操作,我们接下来分析下这个方法:

public void refresh() {
    Environment env = ApplicationModel.getEnvironment();
    try {
        CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
        // 循环方法,获取覆盖值并将新值设置回方法
        Method[] methods = getClass().getMethods();
        for (Method method : methods) {
            if (MethodUtils.isSetter(method)) {
                try {
                    String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                    // 调用 isTypeMatch() 是为了避免重复和错误更新,例如,我们在 ReferenceConfig 中有两个“setGeneric”方法。
                    if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
                        method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
                    }
                } catch (NoSuchMethodException e) {
                    logger.info("Failed to override the property " + method.getName() + " in " +
                            this.getClass().getSimpleName() +
                            ", please make sure every property has getter/setter method provided.");
                }
            } else if (isParametersSetter(method)) {
                String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
                if (StringUtils.isNotEmpty(value)) {
                    Map<String, String> map = invokeGetParameters(getClass(), this);
                    map = map == null ? new HashMap<>() : map;
                    map.putAll(convert(StringUtils.parseParameters(value), ""));
                    invokeSetParameters(getClass(), this, map);
                }
            }
        }
    } catch (Exception e) {
        logger.error("Failed to override ", e);
    }
}

整个方法看起来感觉是有点难度,简单总结起来就是,在配置中心(包括不限于如nacos、apollo、system、propertis)获取对应的配置信息,并注入到对象中。

启动MetadataCenter

private void startMetadataCenter() {
​
    // 如果有需要,使用registry作为元数据中心
    useRegistryAsMetadataCenterIfNecessary();
​
    ApplicationConfig applicationConfig = getApplication();
​
    String metadataType = applicationConfig.getMetadataType();
    // FIXME, multiple metadata config support.
    Collection<MetadataReportConfig> metadataReportConfigs = configManager.getMetadataConfigs();
    if (CollectionUtils.isEmpty(metadataReportConfigs)) {
        // 校验metadataType是否为remote
        if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
            throw new IllegalStateException("No MetadataConfig found, you must specify the remote Metadata Center address when 'metadata=remote' is enabled.");
        }
        return;
    }
    MetadataReportConfig metadataReportConfig = metadataReportConfigs.iterator().next();
    // 校验
    ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
    if (!metadataReportConfig.isValid()) {
        return;
    }
    // 初始化
    MetadataReportInstance.init(metadataReportConfig.toUrl());
}

这里也是流程化的逻辑,不是很难,不做详细赘述,要注意init方法,设置了URL的协议模式;此外,此处根据ApplicationConfig的metadataType是本地或远程,如果选择远程,需要进一步指定元数据中心。

public static void init(URL metadataReportURL) {
    if (init.get()) {
        return;
    }
    // 获取MetadataReportFactory扩展
    MetadataReportFactory metadataReportFactory = ExtensionLoader.getExtensionLoader(MetadataReportFactory.class).getAdaptiveExtension();
    // 如果metadataReportURL的protocol为metadata
    if (METADATA_REPORT_KEY.equals(metadataReportURL.getProtocol())) {
        // 如果URL中的protocol为null,则使用默认的dubbo
        String protocol = metadataReportURL.getParameter(METADATA_REPORT_KEY, DEFAULT_DIRECTORY);
        metadataReportURL = URLBuilder.from(metadataReportURL)
                .setProtocol(protocol)
                .removeParameter(METADATA_REPORT_KEY)
                .build();
    }
    // 通过自适应扩展获取MetadataReport
    metadataReport = metadataReportFactory.getMetadataReport(metadataReportURL);
    init.set(true);
}

MetadataReportInstance的init方法就是通过Dubbo SPI获取其扩展,并指定元数据中心。

初始化MetadataService

private void initMetadataServiceExports() {
    this.metadataServiceExporters = getExtensionLoader(MetadataServiceExporter.class).getSupportedExtensionInstances();
}

MetadataService的初始化很简单,通过Dubbo SPI获取org.apache.dubbo.metadata.MetadataServiceExporter配置文件下的所有扩展。

初始化事件监听器

将DubboBootstrap添加到EventDispatcher(事件调度器)中

校验及更新子配置

private void checkAndUpdateSubConfigs() {
    // 使用默认配置显式定义的全局作用域
    completeCompoundConfigs();
    // 校验默认配置,如果默认的provider为null则新创建一个
    checkDefault();
    // 校验协议,将protocolIds转换成Protocol
    checkProtocol();
    // 初始化一些空配置。
    List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
            .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
    configInitializers.forEach(e -> e.initServiceConfig(this));

    // if protocol is not injvm checkRegistry
    // 如果protocol不是injvm,则需要校验
    if (!isOnlyInJvm()) {
        // 校验注册表
        checkRegistry();
    }
    // 刷新
    this.refresh();

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

    // 判断是否属于GenericService
    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);
        }
        // 检查远程服务接口和方法是否符合Dubbo的要求。主要检查配置文件中配置的方法是否包含在远程服务的接口中
        checkInterfaceAndMethods(interfaceClass, getMethods());
        // 校验Ref
        checkRef();
        generic = Boolean.FALSE.toString();
    }
    if (local != null) {
        if ("true".equals(local)) {
            local = interfaceName + "Local";
        }
        Class<?> localClass;
        try {
            localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(localClass)) {
            throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
        }
    }
    if (stub != null) {
        if ("true".equals(stub)) {
            stub = interfaceName + "Stub";
        }
        Class<?> stubClass;
        try {
            stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(stubClass)) {
            throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
        }
    }
    // 检查stub和local
    checkStubAndLocal(interfaceClass);
    // 检查mock 本地模拟操作的合法性检查和设置。操作可以是带有简单操作的字符串,也可以是其 {@link Class} 实现特定功能的类名
    ConfigValidationUtils.checkMock(interfaceClass, this);
    // 检查ServiceConfig
    ConfigValidationUtils.validateServiceConfig(this);
    // Config配置的后置处理
    postProcessConfig();
}

服务导出

多协议多注册中心导出服务

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)) {
        // 将interfaceName赋值给path
        path = interfaceName;
    }
    // 导出
    doExportUrls();
}
​
 private void doExportUrls() {
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        // 注册服务到ServiceRepository
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
        // 注册Provider到ServiceRepository
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );
​
        // 加载注册表
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
​
        // 遍历ProtocolConfig集合导出每个服务。并在导出服务的过程中,将服务注册到注册中心
        for (ProtocolConfig protocolConfig : protocols) {
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            // 如果用户指定了路径,则再次注册服务,将其映射到路径。
            repository.registerService(pathKey, interfaceClass);
            // TODO, uncomment this line once service key is unified
            serviceMetadata.setServiceKey(pathKey);
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

上面的代码先是分别将服务和Provider注册到ServiceRepository,然后通过loadRegistries加载注册中心链接,最后遍历ProtocolConfig导出每个服务,并在导出服务的过程中,将服务注册到注册中心。

public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
    // check && override if necessary
    List<URL> registryList = new ArrayList<URL>();
    // 获取ApplicationConfig
    ApplicationConfig application = interfaceConfig.getApplication();
    // 获取RegistryConfig
    List<RegistryConfig> registries = interfaceConfig.getRegistries();
    if (CollectionUtils.isNotEmpty(registries)) {
        for (RegistryConfig config : registries) {
            // 获取服务注册地址
            String address = config.getAddress();
            if (StringUtils.isEmpty(address)) {
                // 若为null,设置为0.0.0.0
                address = ANYHOST_VALUE;
            }
            // 如果不为N/A类型
            if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                Map<String, String> map = new HashMap<String, String>();
                // 添加 ApplicationConfig 中的字段信息到 map 中
                AbstractConfig.appendParameters(map, application);
                // 添加 RegistryConfig 字段信息到 map 中
                AbstractConfig.appendParameters(map, config);
                // 添加 path、pid,protocol 等信息到 map 中
                map.put(PATH_KEY, RegistryService.class.getName());
                AbstractInterfaceConfig.appendRuntimeParameters(map);
                if (!map.containsKey(PROTOCOL_KEY)) {
                    map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
                }
                // 解析得到URL列表,address可能包含多个注册中心ip,
                // 因此解析得到的是一个 URL 列表
                List<URL> urls = UrlUtils.parseURLs(address, map);
​
                for (URL url : urls) {
                    url = URLBuilder.from(url)
                            .addParameter(REGISTRY_KEY, url.getProtocol())
                            // 将URL协议头设置为 registry
                            .setProtocol(extractRegistryType(url))
                            .build();
                    // 通过判断条件,决定是否添加 url 到 registryList 中,条件如下:
                    // (服务提供者 && register = true 或 null)
                    //    || (非服务提供者 && subscribe = true 或 null)
                    if ((provider && url.getParameter(REGISTER_KEY, true))
                            || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                        registryList.add(url);
                    }
                }
            }
        }
    }
    return registryList;
}

loadRegistries 方法主要包含如下的逻辑:

  1. 检测是否存在注册中心配置类,不存在则抛出异常
  2. 构建参数映射集合,也就是 map
  3. 构建注册中心链接列表
  4. 遍历链接列表,并根据条件决定是否将其添加到 registryList 中

URL组装

配置检查完毕后,紧接着要做的事情是根据配置,以及其他一些信息组装 URL。因为Dubbo的基础是根据Dubbo SPI来设计的,所以URL贯穿来整个Dubbo,接下来继续分析doExportUrlsFor1Protocol方法

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    String name = protocolConfig.getName();
    // 如果协议名为空,或空串,则将协议名变量设置为 dubbo
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }

    Map<String, String> map = new HashMap<String, String>();
    map.put(SIDE_KEY, PROVIDER_SIDE);

    // 通过反射将对象的字段信息添加到 map 中
    ServiceConfig.appendRuntimeParameters(map);
    AbstractConfig.appendParameters(map, getMetrics());
    AbstractConfig.appendParameters(map, getApplication());
    AbstractConfig.appendParameters(map, getModule());
    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);
    }
    // methods 为 MethodConfig 集合,MethodConfig 中存储了 <dubbo:method> 标签的配置信息
    if (CollectionUtils.isNotEmpty(getMethods())) {
        for (MethodConfig method : getMethods()) {
            // 添加 MethodConfig 对象的字段信息到 map 中,键 = 方法名.属性名。
            // 比如存储 <dubbo:method name="sayHello" retries="2"> 对应的 MethodConfig,
            // 键 = sayHello.retries,map = {"sayHello.retries": 2, "xxx": "yyy"}
            AbstractConfig.appendParameters(map, method, method.getName());
            String retryKey = method.getName() + ".retry";
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                // 检测 MethodConfig retry 是否为 false,若是,则设置重试次数为0
                if ("false".equals(retryValue)) {
                    map.put(method.getName() + ".retries", "0");
                }
            }
            // 获取 ArgumentConfig 列表
            List<ArgumentConfig> arguments = method.getArguments();
            if (CollectionUtils.isNotEmpty(arguments)) {
                for (ArgumentConfig argument : arguments) {
                    // // 检测 type 属性是否为空,或者空串
                    if (argument.getType() != null && argument.getType().length() > 0) {
                        Method[] methods = interfaceClass.getMethods();
                        // visit all methods
                        if (methods.length > 0) {
                            for (int i = 0; i < methods.length; i++) {
                                String methodName = methods[i].getName();
                                // 比对方法名,查找目标方法
                                if (methodName.equals(method.getName())) {
                                    Class<?>[] argtypes = methods[i].getParameterTypes();
                                    // one callback in the method
                                    if (argument.getIndex() != -1) {
                                        // 检测 ArgumentConfig 中的 type 属性与方法参数列表中的参数名称是否一致,不一致则抛出异常
                                        if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                            // 添加 ArgumentConfig 字段信息到 map 中,
                                            // 键前缀 = 方法名.index,比如:
                                            // map = {"sayHello.3": true}
                                            AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                        } else {
                                            throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                        }
                                    } else {
                                        // multiple callbacks in the method
                                        for (int j = 0; j < argtypes.length; j++) {
                                            Class<?> argclazz = argtypes[j];
                                            // 从参数类型列表中查找类型名称为 argument.type 的参数
                                            if (argclazz.getName().equals(argument.getType())) {
                                                AbstractConfig.appendParameters(map, argument, method.getName() + "." + j);
                                                if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                    throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        // 用户未配置 type 属性,但配置了 index 属性,且 index != -1
                    } else if (argument.getIndex() != -1) {
                        // 添加 ArgumentConfig 字段信息到 map 中
                        AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                    } else {
                        throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                    }

                }
            }
        } // end of methods for
    }

    // 检测 generic 是否为 "true",并根据检测结果向 map 中添加不同的信息
    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);
        }
        // 为接口生成包裹类 Wrapper,Wrapper 中包含了接口的详细信息,比如接口方法名数组,字段信息等
        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        // 添加方法名到 map 中,如果包含多个方法名,则用逗号隔开,比如 method = init,destroy
        if (methods.length == 0) {
            logger.warn("No method found in service interface " + interfaceClass.getName());
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            // 将逗号作为分隔符连接方法名,并将连接后的字符串放入 map 中
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }

    // 添加 token 到 map 中
    if(ConfigUtils.isEmpty(token) && provider != null) {
        token = provider.getToken();
    }

    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {
            // 随机生成 token
            map.put(TOKEN_KEY, UUID.randomUUID().toString());
        } else {
            map.put(TOKEN_KEY, token);
        }
    }

    // 初始化ServiceMetadata中的attachments
    serviceMetadata.getAttachments().putAll(map);

    // 获取host
    String host = findConfigedHosts(protocolConfig, registryURLs, map);
    // 获取port
    Integer port = findConfigedPorts(protocolConfig, name, map);
    // 组装URL
    URL url = new URL(name, 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)) {
                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 = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                    }
                    if (logger.isInfoEnabled()) {
                        if (url.getParameter(REGISTER_KEY, true)) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        } else {
                            logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                        }
                    }

                    // 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);
                    }
                    // 为服务提供类(ref)生成 Invoker
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                    // DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    // 导出服务,并生成 Exporter,此处的PROTOCOL为Dubbo SPI生成的自适应扩展类
                    // 再不配置其他包装类的情况下,首先会执行ProtocolFilterWrapper的export,再执行ProtocolListenerWrapper的export
                    // 最后执行RegistryProtocol的export
                    Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                    exporters.add(exporter);
                }
                // 不存在注册中心,仅导出服务
            } else {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                exporters.add(exporter);
            }
            /**
             * @since 2.7.0
             * ServiceData Store
             */
            WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
            if (metadataService != null) {
                metadataService.publishServiceDefinition(url);
            }
        }
    }
    this.urls.add(url);
}

上面的代码首先是将一些信息,比如版本、时间戳、方法名以及各种配置对象的字段信息放入到 map 中,map 中的内容将作为 URL 的查询字符串。构建好 map 后,紧接着是获取上下文路径、主机名以及端口号等信息。最后将 map 和主机名等数据传给 URL 构造方法创建 URL 对象。需要注意的是,这里出现的 URL 并非 java.net.URL,而是 com.alibaba.dubbo.common.URL。其中MethodConfig配置的解析嵌套太深,但无非就是根据配置解析完毕后,将对应的信息存储到map中。map构建完毕后,接下来就是根据map及serviceConfig组装URL。最后就是执行导出服务。

导出服务

我们再来看下doExportUrlsFor1Protocol方法,其根据 url 中的 scope 参数决定服务导出方式,分别如下:

  • scope = none,不导出服务
  • scope != remote,导出到本地
  • scope != local,导出到远程

不管是导出到本地,还是远程。进行服务导出之前,均需要先创建 Invoker,这是一个很重要的步骤。因此下面先来分析 Invoker 的创建过程。先来分析下Invoker是如何创建的,再来分析服务导出.

Invoker创建

在 Dubbo 中,Invoker 是一个非常重要的模型。在服务提供端,以及服务引用端均会出现 Invoker。Dubbo 官方文档中对 Invoker 进行了说明,这里引用一下。

Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

既然 Invoker 如此重要,那么我们很有必要搞清楚 Invoker 的用途。Invoker 是由 ProxyFactory 创建而来,Dubbo 默认的 ProxyFactory 实现类是 JavassistProxyFactory。下面我们到 JavassistProxyFactory 代码中,探索 Invoker 的创建过程。如下:

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // 为目标类创建 Wrapper Wrapper类不能正确地处理这个场景:classname 包含'$'
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    // 创建匿名 Invoker 类对象,并实现 doInvoke 方法。
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            // 调用 Wrapper 的 invokeMethod 方法,invokeMethod 最终会调用目标方法
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

如上,JavassistProxyFactory 创建了一个继承自 AbstractProxyInvoker 类的匿名对象,并覆写了抽象方法 doInvoke。覆写后的 doInvoke 逻辑比较简单,仅是将调用请求转发给了 Wrapper 类的 invokeMethod 方法。Wrapper 用于“包裹”目标类,Wrapper 是一个抽象类,仅可通过 getWrapper(Class) 方法创建子类。在创建 Wrapper 子类的过程中,子类代码生成逻辑会对 getWrapper 方法传入的 Class 对象进行解析,拿到诸如类方法,类成员变量等信息。以及生成 invokeMethod 方法代码和其他一些方法代码。代码生成完毕后,通过 Javassist 生成 Class 对象,最后再通过反射创建 Wrapper 实例。相关的代码如下:

public static Wrapper getWrapper(Class<?> c) {
    while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
    {
        c = c.getSuperclass();
    }

    if (c == Object.class) {
        return OBJECT_WRAPPER;
    }

    Wrapper wrapper = WRAPPER_MAP.computeIfAbsent(c, key -> makeWrapper(key));
    return wrapper;
}

getWrapper 方法仅包含一些缓存操作逻辑,不难理解。下面我们看一下 makeWrapper 方法。

private static Wrapper makeWrapper(Class<?> c) {
    // 检测 c 是否为基本类型,若是则抛出异常
    if (c.isPrimitive()) {
        throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
    }

    String name = c.getName();
    ClassLoader cl = ClassUtils.getClassLoader(c);

    // c1 用于存储 setPropertyValue 方法代码
    StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
    // c2 用于存储 getPropertyValue 方法代码
    StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
    // c3 用于存储 invokeMethod 方法代码
    StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");

    // 生成类型转换代码及异常捕捉代码,比如:
    //   DemoService w; try { w = ((DemoServcie) $1); }}catch(Throwable e){ throw new IllegalArgumentException(e); }
    c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
    c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
    c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");

    // pts 用于存储成员变量名和类型
    Map<String, Class<?>> pts = new HashMap<>();
    // ms 用于存储方法描述信息(可理解为方法签名)及 Method 实例
    Map<String, Method> ms = new LinkedHashMap<>();
    // mns 为方法名列表
    List<String> mns = new ArrayList<>();
    // dmns 用于存储“定义在当前类中的方法”的名称
    List<String> dmns = new ArrayList<>();

    // 获取 public 访问级别的字段,并为所有字段生成条件判断语句
    for (Field f : c.getFields()) {
        String fn = f.getName();
        Class<?> ft = f.getType();
        if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) {
            // 忽略关键字 static 或 transient 修饰的变量
            continue;
        }

        // 生成条件判断及赋值语句,比如:
        // if( $2.equals("name") ) { w.name = (java.lang.String) $3; return;}
        // if( $2.equals("age") ) { w.age = ((Number) $3).intValue(); return;}
        c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
        // 生成条件判断及返回语句,比如:
        // if( $2.equals("name") ) { return ($w)w.name; }
        c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
        // 存储 <字段名, 字段类型> 键值对到 pts 中
        pts.put(fn, ft);
    }

    Method[] methods = c.getMethods();
    // 检测 c 中是否包含在当前类中声明的方法
    boolean hasMethod = hasMethods(methods);
    if (hasMethod) {
        c3.append(" try{");
        for (Method m : methods) {
            // 忽略 Object 中定义的方法
            if (m.getDeclaringClass() == Object.class) {
                continue;
            }

            String mn = m.getName();
            // 生成方法名判断语句,比如:
            // if ( "sayHello".equals( $2 )
            c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
            int len = m.getParameterTypes().length;
            // 生成“运行时传入的参数数量与方法参数列表长度”判断语句,比如:
            // && $3.length == 2
            c3.append(" && ").append(" $3.length == ").append(len);

            boolean override = false;
            for (Method m2 : methods) {
                // 检测方法是否存在重载情况,条件为:方法对象不同 && 方法名相同
                if (m != m2 && m.getName().equals(m2.getName())) {
                    override = true;
                    break;
                }
            }

            // 对重载方法进行处理,考虑下面的方法:
            //    1. void sayHello(Integer, String)
            //    2. void sayHello(Integer, Integer)
            // 方法名相同,参数列表长度也相同,因此不能仅通过这两项判断两个方法是否相等。
            // 需要进一步判断方法的参数类型
            if (override) {
                if (len > 0) {
                    for (int l = 0; l < len; l++) {
                        // 生成参数类型进行检测代码,比如:
                        // && $3[0].getName().equals("java.lang.Integer")
                        //    && $3[1].getName().equals("java.lang.String")
                        c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
                                .append(m.getParameterTypes()[l].getName()).append("\")");
                    }
                }
            }

            // 添加 ) {,完成方法判断语句,此时生成的代码可能如下(已格式化):
            // if ("sayHello".equals($2)
            //     && $3.length == 2
            //     && $3[0].getName().equals("java.lang.Integer")
            //     && $3[1].getName().equals("java.lang.String")) {
            c3.append(" ) { ");

            // 根据返回值类型生成目标方法调用语句
            if (m.getReturnType() == Void.TYPE) {
                // w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]); return null;
                c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
            } else {
                // return w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]);
                c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
            }

            // 添加 }, 生成的代码形如(已格式化):
            // if ("sayHello".equals($2)
            //     && $3.length == 2
            //     && $3[0].getName().equals("java.lang.Integer")
            //     && $3[1].getName().equals("java.lang.String")) {
            //
            //     w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]);
            //     return null;
            // }
            c3.append(" }");

            // 添加方法名到 mns 集合中
            mns.add(mn);
            // 检测当前方法是否在 c 中被声明的
            if (m.getDeclaringClass() == c) {
                // 若是,则将当前方法名添加到 dmns 中
                dmns.add(mn);
            }
            ms.put(ReflectUtils.getDesc(m), m);
        }
        // 添加异常捕捉语句
        c3.append(" } catch(Throwable e) { ");
        c3.append("     throw new java.lang.reflect.InvocationTargetException(e); ");
        c3.append(" }");
    }

    // 添加 NoSuchMethodException 异常抛出代码
    c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");

    // 处理 get/set 方法
    Matcher matcher;
    for (Map.Entry<String, Method> entry : ms.entrySet()) {
        String md = entry.getKey();
        Method method = entry.getValue();
        // 匹配以 get 开头的方法
        if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
            // 获取属性名
            String pn = propertyName(matcher.group(1));
            // 生成属性判断以及返回语句,示例如下:
            // if( $2.equals("name") ) { return ($w).w.getName(); }
            c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
            pts.put(pn, method.getReturnType());
        // 匹配以 is/has/can 开头的方法
        } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
            String pn = propertyName(matcher.group(1));
            // 生成属性判断以及返回语句,示例如下:
            // if( $2.equals("dream") ) { return ($w).w.hasDream(); }
            c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
            pts.put(pn, method.getReturnType());
        // 匹配以 set 开头的方法
        } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
            Class<?> pt = method.getParameterTypes()[0];
            String pn = propertyName(matcher.group(1));
            // 生成属性判断以及 setter 调用语句,示例如下:
            // if( $2.equals("name") ) { w.setName((java.lang.String)$3); return; }
            c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt, "$3")).append("); return; }");
            pts.put(pn, pt);
        }
    }
    // 添加 NoSuchPropertyException 异常抛出代码
    c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
    c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");

    // make class
    long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
    // 创建类生成器
    ClassGenerator cc = ClassGenerator.newInstance(cl);
    // 设置类名及超类
    cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
    cc.setSuperClass(Wrapper.class);
    // 添加默认构造方法
    cc.addDefaultConstructor();
    // 添加方法代码
    cc.addField("public static String[] pns;");
    cc.addField("public static " + Map.class.getName() + " pts;");
    cc.addField("public static String[] mns;");
    cc.addField("public static String[] dmns;");
    for (int i = 0, len = ms.size(); i < len; i++) {
        cc.addField("public static Class[] mts" + i + ";");
    }

    cc.addMethod("public String[] getPropertyNames(){ return pns; }");
    cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
    cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
    cc.addMethod("public String[] getMethodNames(){ return mns; }");
    cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
    cc.addMethod(c1.toString());
    cc.addMethod(c2.toString());
    cc.addMethod(c3.toString());

    try {
        // 生成类
        Class<?> wc = cc.toClass();
        // 设置字段值
        wc.getField("pts").set(null, pts);
        wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
        wc.getField("mns").set(null, mns.toArray(new String[0]));
        wc.getField("dmns").set(null, dmns.toArray(new String[0]));
        int ix = 0;
        for (Method m : ms.values()) {
            wc.getField("mts" + ix++).set(null, m.getParameterTypes());
        }
        // 创建 Wrapper 实例
        return (Wrapper) wc.newInstance();
    } catch (RuntimeException e) {
        throw e;
    } catch (Throwable e) {
        throw new RuntimeException(e.getMessage(), e);
    } finally {
        cc.release();
        ms.clear();
        mns.clear();
        dmns.clear();
    }
}

对于这种代码生成的方法,个人感觉没有太多要分析的,下面贴出一段由此方法生成的Wrappe示例如:

package org.apache.dubbo.demo.provider.test;

import org.apache.dubbo.common.bytecode.Wrapper;

public class Wrapper$sw0 extends Wrapper {

    public static String[] pns;

    public static java.util.Map pts;

    public static String[] mns;

    public static String[] dmns;

    public static Class[] mts0;

    public static Class[] mts1;

    @Override
    public String[] getPropertyNames(){
        return pns;
    }

    @Override
    public boolean hasProperty(String n){
        return pts.containsKey(n);
    }

    @Override
    public Class getPropertyType(String n){
        return (Class)pts.get(n);
    }

    @Override
    public String[] getMethodNames(){
        return mns;
    }

    @Override
    public String[] getDeclaredMethodNames(){
        return dmns;
    }

    @Override
    public void setPropertyValue(Object o, String n, Object v){
        org.apache.dubbo.demo.DemoService w;
        try{
            w = ((org.apache.dubbo.demo.DemoService)o);
        }catch(Throwable e){
            throw new IllegalArgumentException(e);
        }
        throw new org.apache.dubbo.common.bytecode.NoSuchPropertyException("Not found property \""+n+"\" field or " + "setter method in class org.apache.dubbo.demo.DemoService.");
    }

    @Override
    public Object getPropertyValue(Object o, String n){
        org.apache.dubbo.demo.DemoService w;
        try{
            w = ((org.apache.dubbo.demo.DemoService)o);
        }catch(Throwable e){
            throw new IllegalArgumentException(e);
        }
        throw new org.apache.dubbo.common.bytecode.NoSuchPropertyException("Not found property \""+n+"\" field or " + "setter method in class org.apache.dubbo.demo.DemoService.");
    }

    @Override
    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{
        org.apache.dubbo.demo.DemoService w;
        try{
            w = ((org.apache.dubbo.demo.DemoService)o);
        }catch(Throwable e){
            throw new IllegalArgumentException(e);
        }

        try{
            if( "sayHelloAsync".equals(n)  &&  p.length == 1 ) {
                return w.sayHelloAsync((String)v[0]);
            }
            if( "sayHello".equals(n)  &&  p.length == 1 ) {
                return w.sayHello((String)v[0]);
            }
        } catch(Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+ n +"\" in class " + "org.apache.dubbo.demo.DemoService."); }
}

导出服务到本地

private void exportLocal(URL url) {
    URL local = URLBuilder.from(url)
            // 设置为InjvmProtocol
            .setProtocol(LOCAL_PROTOCOL)
            .setHost(LOCALHOST_VALUE)
            .setPort(0)
            .build();
    // 执行导出:先是执行Protocol的包装类,框架内置的有ProtocolFilterWrapper>ProtocolListenerWrapper
    // 最后在优先级最低的包装类的export方法中执行导出,此处最后执行的是InjvmProtocol的export方法
    Exporter<?> exporter = PROTOCOL.export(
            // 通过ProxyFactory获取Invoker,默认为JavassistProxyFactory
            PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local)
    );
    // 添加到缓存中
    exporters.add(exporter);
    logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}

这个方法做了以下几件事:

  1. 将传递过来的URL重新构建,将Protocol设置为InjvmProtocol,host设置为127.0.0.1,port设置为0
  2. 获取Invoker
  3. 执行export方法导出服务
  4. 将Exporter方法缓存

PROTOCOL为protocol的自适应扩展类,将根据URL的协议获取扩展类,这里获取到的InjvmProtocol,最后执行的顺序是(未添加其他Protocol的包装类的情况下):ProtocolFilterWrapper->ProtocolListenerWrapper->InjvmProtocol,我们再来看下InjvmProtocol的export方法

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 构造InjvmExporter返回
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

如上,InjvmProtocol 的 export 方法仅创建了一个 InjvmExporter,无其他逻辑。到此导出服务到本地就分析完了,接下来,我们继续分析导出服务到远程的过程。

导出服务到远程

同理,PROTOCOL为protocol的自适应扩展类,将根据URL的协议获取扩展类,这里获取到的RegistryProtocol,最后执行的顺序是(未添加其他Protocol的包装类的情况下):ProtocolFilterWrapper->ProtocolListenerWrapper->InjvmProtocol,我们再来看下RegistryProtocol的export方法:>

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // 获取注册中心 URL,以 zookeeper 注册中心为例,得到的示例 URL 如下:
    // zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2
    // &export=dubbo%3A%2F%2F172.17.48.52%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider
    URL registryUrl = getRegistryUrl(originInvoker);
​
    // 获取provider的url
    // dubbo://192.168.1.101:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider
    // &bind.ip=192.168.1.101&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true
    // &generic=false&interface=org.apache.dubbo.demo.DemoService&metadata-type=remote&methods=sayHello,sayHelloAsync
    // &pid=15770&release=&side=provider&timestamp=1635326008251
    URL providerUrl = getProviderUrl(originInvoker);
​
    // 订阅URL,比如:
    // provider://192.168.1.101:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider
    // &bind.ip=192.168.1.101&bind.port=20880&category=configurators&check=false&default=true&deprecated=false
    // &dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&metadata-type=remote
    // &methods=sayHello,sayHelloAsync&pid=15770&release=&side=provider&timestamp=1635326008251
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
​
    // TODO
    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
​
    // 导出Export
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
​
    // 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry
    final Registry registry = getRegistry(originInvoker);
    final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
​
    // 是否需要延迟加载
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        // 向注册中心注册服务
        register(registryUrl, registeredProviderUrl);
    }
​
    // 在提供程序模型上注册声明的url
    registerStatedUrl(registryUrl, registeredProviderUrl, register);
​
​
    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
​
    // 弃用!订阅以覆盖2.6中的规则。x或之前。
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
​
    notifyExport(exporter);
    // 确保每次导出时都返回一个新的导出器实例
    return new DestroyableExporter<>(exporter);
}

上面代码看起来比较复杂,主要做如下一些操作:

  1. 调用 doLocalExport 导出服务
  2. 向注册中心注册服务
  3. 向注册中心进行订阅 override 数据
  4. 创建并返回 DestroyableExporter
doLocalExport

在以上操作中,除了创建并返回 DestroyableExporter 没什么难度外,其他几步操作都不是很简单。这其中,导出服务和注册服务是本章要重点分析的逻辑。 订阅 override 数据并非本文重点内容,后面会简单介绍一下。下面先来分析 doLocalExport 方法的逻辑,如下:

private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
    String key = getCacheKey(originInvoker);
    // 访问并设置缓存
    return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
        // 创建Invoker为委托对象
        Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
        // 执行Protocol的export方法,默认为DubboProtocol,可在ProtocolConfig中的设置name进行设置,如设置为RedisProtocol,:protocolConfig.setName("redis");
        return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
    });
}

这里的逻辑看起来很简单,首先是构建Invoker实例,然后执行export方法,最后构建ExporterChangeableWrapper实例。这里需要关注的有两点,第一点是方法中protocol实例是哪种类型,第二点是具体执行的export方法。经过上面的分析我们知道doLocalExport的上一级方法为RegistryProtocol的export方法,RegistryProtocol的export上一级方法为ServiceConfig中的PROTOCOL.export方法,上面我们分析出ServiceConfig中的PROTOCOL为protocl的自适应扩展类,在该自适应扩展类中,解析URL获取到的protocol为registry,然后通过getExtension(“registry”)方法获取到扩展类RegistryProtocol,在SPI中我们分析过,Dubbo SPI之于java SPI多了一个IOC注入的过程,这里在获取到了RegistryProtocol后对其进行了IOC注入,最后RegistryProtocol的protocl注入到类型为Protocol的自适应扩展类,到这里我们就可以知道RegistryProtocol中的protocol为自适应扩展类,接着会在该扩展类中执行export方法,通过解析URL获取到具体Protocol的扩展,这里默认的为DubboProtocol,可修改ProtocolConfig中name属性进行修改,如设置为RedisProtocol:protocolConfig.setName("redis");,接下来分析DubboProtocol中的export方法:

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();

    // 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如:
    // demoGroup/com.alibaba.dubbo.demo.DemoService:1.0.1:20880
    String key = serviceKey(url);
    // 创建 DubboExporter
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    // 将 <key, exporter> 键值对放入缓存中
    exporterMap.put(key, exporter);

    // 导出用于分派事件的存根服务
    Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
    Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
    if (isStubSupportEvent && !isCallbackservice) {
        String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
        if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
            if (logger.isWarnEnabled()) {
                logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
                        "], has set stubproxy support event ,but no stub methods founded."));
            }

        }
    }

    // 启动服务器
    openServer(url);
    // 优化序列化
    optimizeSerialization(url);

    return exporter;
}

我们重点关注 DubboExporter 的创建以及 openServer 方法,其他逻辑看不懂也没关系,不影响理解服务导出过程。另外,DubboExporter 的代码比较简单,就不分析了。下面分析 openServer 方法。

private void openServer(URL url) {
    // 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
    String key = url.getAddress();
    // 客户端可以导出只供服务器调用的服务
    boolean isServer = url.getParameter(IS_SERVER_KEY, true);
    if (isServer) {
        // 访问缓存
        ProtocolServer server = serverMap.get(key);
        if (server == null) {
            synchronized (this) {
                // 访问缓存
                server = serverMap.get(key);
                if (server == null) {
                    // 创建服务
                    serverMap.put(key, createServer(url));
                }
            }
        } else {
            // 服务器已创建,则根据 url 中的配置重置服务器
            server.reset(url);
        }
    }
}

如上,在同一台机器上(单网卡),同一个端口上仅允许启动一个服务器实例。若某个端口上已有服务器实例,此时则调用 reset 方法重置服务器的一些配置。考虑到篇幅问题,关于服务器实例重置的代码就不分析了。接下来分析服务器实例的创建过程。如下:

private ProtocolServer createServer(URL url) {
    url = URLBuilder.from(url)
            // 当服务器关闭时发送只读事件,默认为启用
            .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
            // 默认启用心跳
            .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
            // 添加编码解码器参数
            .addParameter(CODEC_KEY, DubboCodec.NAME)
            .build();
    // 获取 server 参数,默认为 netty
    String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);

    // 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
    if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
        throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    }

    ExchangeServer server;
    try {
        // 创建 ExchangeServer
        server = Exchangers.bind(url, requestHandler);
    } catch (RemotingException e) {
        throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    }

    // 获取 client 参数,可指定 netty,mina
    str = url.getParameter(CLIENT_KEY);
    if (str != null && str.length() > 0) {
        // 获取所有的 Transporter 实现类名称集合,比如 supportedTypes = [netty, mina]
        Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
        // 检测当前 Dubbo 所支持的 Transporter 实现类名称列表中,
        // 是否包含 client 所表示的 Transporter,若不包含,则抛出异常
        if (!supportedTypes.contains(str)) {
            throw new RpcException("Unsupported client type: " + str);
        }
    }

    return new DubboProtocolServer(server);
}

createServer 包含三个核心的逻辑。第一是检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常。第二是创建服务器实例。第三是检测是否支持 client 参数所表示的 Transporter 拓展,不存在也是抛出异常。两次检测操作所对应的代码比较直白了,无需多说。但创建服务器的操作目前还不是很清晰,我们继续往下看。

public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    if (url == null) {
        throw new IllegalArgumentException("url == null");
    }
    if (handler == null) {
        throw new IllegalArgumentException("handler == null");
    }
    url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
    // 获取 Exchanger,默认为 HeaderExchanger。
    // 紧接着调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例
    return getExchanger(url).bind(url, handler);
}

上面代码比较简单,就不多说了。下面看一下 HeaderExchanger 的 bind 方法。

public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    // 创建 HeaderExchangeServer 实例,该方法包含了多个逻辑,分别如下:
    //   1. new HeaderExchangeHandler(handler)
    //  2. new DecodeHandler(new HeaderExchangeHandler(handler))
    //   3. Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))
    return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}

HeaderExchanger 的 bind 方法包含的逻辑比较多,但目前我们仅需关心 Transporters 的 bind 方法逻辑即可。该方法的代码如下:

public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {
    if (url == null) {
        throw new IllegalArgumentException("url == null");
    }
    if (handlers == null || handlers.length == 0) {
        throw new IllegalArgumentException("handlers == null");
    }
    ChannelHandler handler;
    if (handlers.length == 1) {
        handler = handlers[0];
    } else {
        // 如果 handlers 元素数量大于1,则创建 ChannelHandler 分发器
        handler = new ChannelHandlerDispatcher(handlers);
    }
    // 获取自适应 Transporter 实例,并调用实例方法
    return getTransporter().bind(url, handler);
}

getTransporter() 方法获取的 Transporter 是在运行时动态创建的,类名为 TransporterAdaptive,也就是自适应拓展类。TransporterAdaptive,也就是自适应拓展类。TransporterAdaptive 会在运行时根据传入的 URL 参数决定加载什么类型的 Transporter,默认为 NettyTransporter。下面我们继续跟下去,这次分析的是 NettyTransporter 的 bind 方法。

public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
    // 创建 NettyServer
    return new NettyServer(url, handler);
}

这里仅有一句创建 NettyServer 的代码,无需多说,我们继续向下看。

public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    // 您可以自定义客户端线程池的名称和类型:THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
    // 处理程序将被包装: MultiMessageHandler->HeartbeatHandler->handler
    // 调用父类构造方法
    super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}


public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        // 调用父类构造方法,这里就不用跟进去了,没什么复杂逻辑
        super(url, handler);

        localAddress = getUrl().toInetSocketAddress();

        // 获取 ip 和端口
        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
            // 设置 ip 为 0.0.0.0
            bindIp = ANYHOST_VALUE;
        }

        bindAddress = new InetSocketAddress(bindIp, bindPort);
        // 获取最大可接受连接数
        this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
        try {
            // 调用模板方法 doOpen 启动服务器
            doOpen();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                    + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        executor = executorRepository.createExecutorIfAbsent(url);
    }

上面代码多为赋值代码,不需要多讲。我们重点关注 doOpen 抽象方法,该方法需要子类实现。下面回到 NettyServer 中。

protected void doOpen() throws Throwable {
    // 构建netty server bootstrap
    bootstrap = new ServerBootstrap();
    // 创建boss线程池
    bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
    // 创建worker线程池
    workerGroup = NettyEventLoopFactory.eventLoopGroup(
            getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
            "NettyServerWorker");
​
    final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
    channels = nettyServerHandler.getChannels();
​
    bootstrap.group(bossGroup, workerGroup)
            .channel(NettyEventLoopFactory.serverSocketChannelClass())
            .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
            .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
            .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    // FIXME: should we use getTimeout()?
                    int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
                    NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                    if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
                        ch.pipeline().addLast("negotiation",
                                SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
                    }
                    ch.pipeline()
                            .addLast("decoder", adapter.getDecoder())
                            .addLast("encoder", adapter.getEncoder())
                            .addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                            .addLast("handler", nettyServerHandler);
                }
            });
    // 绑定到指定的 ip 和端口上
    ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
    channelFuture.syncUninterruptibly();
    channel = channelFuture.channel();
}

以上就是 NettyServer 创建的过程,dubbo 2.7.8默认使用的 NettyServer 是基于 netty 4.x 版本实现的,Dubbo 另外提供了 netty 3.x 版本的 NettyServer,大家可在使用 Dubbo 的过程中按需进行配置。

到此,关于服务导出的过程就分析完了。整个过程比较复杂,大家在分析的过程中耐心一些。并且多写 Demo 进行调试,以便能够更好的理解代码逻辑。

本节内容先到这里,接下来分析服务导出的另一块逻辑 — 服务注册。在此回到export方法看register方法:

服务注册

服务注册操作对于 Dubbo 来说不是必需的,通过服务直连的方式就可以绕过注册中心。但通常我们不会这么做,直连方式不利于服务治理,仅推荐在测试服务时使用。对于 Dubbo 来说,注册中心虽不是必需,但却是必要的。因此,关于注册中心以及服务注册相关逻辑,我们也需要搞懂。

接下来以Nacos 注册中心作为分析目标,其他类型注册中心大家可自行分析。>

private void register(URL registryUrl, URL registeredProviderUrl) {
    // 获取 Registry
    Registry registry = registryFactory.getRegistry(registryUrl);
    // 注册服务
    registry.register(registeredProviderUrl);
}

register 方法包含两步操作,第一步是获取注册中心实例,第二步是向注册中心注册服务。接下来分两节内容对这两步操作进行分析。

获取注册中心实例

本节以Nacos注册中心为例进行分析,registryFactory.getRegistry(registryUrl)方法中,registryFactory为RegistryFacotry的自适应扩展类,其执行getRegistry方法时,会通过ExtensionLoader.getExtensionLoader(RegsitryFactory.class).getExtension("nacos")获取到NacosRegistryFactory的包装类RegistryFactoryWrapper,最后在该扩展类中执行RegistryFactoryWrapper的getRegistry方法,我们先来看下RegistryFactoryWrapper方法中的getRegistry方法:

public Registry getRegistry(URL url) {
    return new ListenerRegistryWrapper(registryFactory.getRegistry(url),
            Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(RegistryServiceListener.class)
                    .getActivateExtension(url, "registry.listeners")));
}

这里执行逻辑有:>

  1. 通过registryFactory获取Registry实例
  2. 通过Dubbo SPI获取RegistryServiceListener
  3. 返回Registry的包装类ListenerRegistryWrapper

首先来分析下registryFactory获取Registry实例,这里的registryFacotry为NacosRegistryFacotry,NacosRegistryFactory为AbstractRegistryFacotry的子类,其getRegistry方法在其父类AbstractRegistryFacotry中,接下来分析下该方法:

public Registry getRegistry(URL url) {
    if (destroyed.get()) {
        LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " +
                "Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of.");
        return DEFAULT_NOP_REGISTRY;
    }
​
    url = URLBuilder.from(url)
            .setPath(RegistryService.class.getName())
            .addParameter(INTERFACE_KEY, RegistryService.class.getName())
            .removeParameters(EXPORT_KEY, REFER_KEY)
            .build();
    String key = createRegistryCacheKey(url);
    // Lock the registry access process to ensure a single instance of the registry
    LOCK.lock();
    try {
        // 访问缓存
        Registry registry = REGISTRIES.get(key);
        if (registry != null) {
            return registry;
        }
        // 缓存未命中,创建 Registry 实例
        registry = createRegistry(url);
        if (registry == null) {
            throw new IllegalStateException("Can not create registry " + url);
        }
        // 写入缓存
        REGISTRIES.put(key, registry);
        return registry;
    } finally {
        // Release the lock
        LOCK.unlock();
    }
}

getRegistry 方法先访问缓存,缓存未命中则调用 createRegistry 创建 Registry,然后写入缓存。这里的 createRegistry 是一个模板方法,由具体的子类实现。因此,下面我们回到 NacosRegistryFactory 中探究一番。

@Override
protected Registry createRegistry(URL url) {
    return new NacosRegistry(url, createNamingService(url));
}

继续看createNamingService方法

public static NamingService createNamingService(URL connectionURL) {
    // 获取nacos配置
    Properties nacosProperties = buildNacosProperties(connectionURL);
    NamingService namingService;
    try {
        // 创建NamingService
        namingService = NacosFactory.createNamingService(nacosProperties);
    } catch (NacosException e) {
        if (logger.isErrorEnabled()) {
            logger.error(e.getErrMsg(), e);
        }
        throw new IllegalStateException(e);
    }
    return namingService;
}

这个方法逻辑也不多,首先根据URL获取nacos配置,再通过NacosFactory创建NamingService并返回。到这里,就通过NacosRegistryFacotry获取到了Registry实例。在回到这里,第一步逻辑分析完了,第二步是通过Dubbo SPI获取扩展类,这里不再分析,我们在Dubbo SPI章节中有详细分析过,最后是通过第一步和第二步获取到的实例构建Registry的包装类ListenerRegistryWrapper并返回。到这里,获取注册中心实例接分析完毕了。

注册服务

回到RegistryProtocol的register方法,接着看registry.register(registeredProviderUrl)这个步骤,上一步获取到的注册中心实例为Registry的包装类ListenerRegistryWrapper,接下来我们看下该包装类的register方法:

public void register(URL url) {
    try {
        // 注册 NacosRegistry
        registry.register(url);
    } finally {
        if (CollectionUtils.isNotEmpty(listeners)) {
            RuntimeException exception = null;
            for (RegistryServiceListener listener : listeners) {
                if (listener != null) {
                    try {
                        // 注册监听
                        listener.onRegister(url);
                    } catch (RuntimeException t) {
                        logger.error(t.getMessage(), t);
                        exception = t;
                    }
                }
            }
            if (exception != null) {
                throw exception;
            }
        }
    }
}

这里的逻辑只有两个,通过NacosRegistry进行注册,注册结束后监听该注册,再接着分析NacosRegistry的register方法,这个方法定义在 FailbackRegistry 抽象类中。代码如下:

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 {
        // 向服务器端发送注册请求
        doRegister(url);
    } catch (Exception e) {
        Throwable t = e;

        // 如果启动检测被打开,则直接抛出Exception.
        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);
        }

        // 将失败的注册请求记录到失败列表中,定期重试
        addFailedRegistered(url);
    }
}

我们重点关注 doRegister 方法调用即可,其他的代码先忽略。doRegister 方法是一个模板方法,因此我们到 FailbackRegistry 子类 NacosRegistry 中进行分析。如下:

public void doRegister(URL url) {
    // 获取服务名称
    final String serviceName = getServiceName(url);
    // 构建nacos实例,主要是一些赋值操作
    final Instance instance = createInstance(url);
    // 执行注册
    execute(namingService -> namingService.registerInstance(serviceName,
            getUrl().getParameter(GROUP_KEY, Constants.DEFAULT_GROUP), instance));
}

到这里服务注册就执行完毕了。