Dubbo的服务注册和暴露

132 阅读8分钟

DubboBootStrap 监听实现

public DubboBootstrap start() {
    //1. 判断是不是没有启动,如果是就将true设置给started表示,然后执行启动程序
    if (started.compareAndSet(false, true)) {
        ready.set(false);
        //2. 初始化
        initialize();
        if (logger.isInfoEnabled()) {
            logger.info(NAME + " is starting...");
        }
        // 3. export Dubbo Services 暴露Provider的 Services  服务暴露
        exportServices();

        // 判断是否是进行服务暴力
        if (!isOnlyRegisterProvider() || hasExportedServices()) {
            // 4.导出元数据 export MetadataService
            exportMetadataService();
            //5. Register the local ServiceInstance if required通过spi方式获取服务注册中心
            registerServiceInstance();
        }
            //6 处理Consumer的ReferenceConfig  服务引用
        referServices();
        if (asyncExportingFutures.size() > 0) {//异步操作,在exportServices中添加
            new Thread(() -> {
                try {
                    this.awaitFinish();
                } catch (Exception e) {
                    logger.warn(NAME + " exportAsync occurred an exception.");
                }
                ready.set(true);
                if (logger.isInfoEnabled()) {
                    logger.info(NAME + " is ready.");
                }
            }).start();
        } else {
            ready.set(true);
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is ready.");
            }
        }
        if (logger.isInfoEnabled()) {
            logger.info(NAME + " has started.");
        }
    }
    return this;
}

dubboBootStrap 初始化

public void initialize() {
    //CAS操作确认是否初始化
    if (!initialized.compareAndSet(false, true)) {
        return;
    }

    //1. 初始化框架的扩展
    //这个实现就是从系统中获取SPI接口FrameworkExt的所有的扩展实例,然后依次调用initialize方法
    //iniFrameworkExts == Environment#initialize()
    ApplicationModel.initFrameworkExts();
    //2. 初始化配置中心
    //   startConfigCenter中第一条语句,当注册中心使用zookeeper时,如果配置中心地址没有指定就以注册中心zookeeper作为默认的配置中心
    startConfigCenter();
    //4. 加载远程的配置
    loadRemoteConfigs();
    //5. 检查全局配置
    checkGlobalConfigs();

    // @since 2.7.8
    startMetadataCenter();
    //6. 初始化元数据操作接口
    initMetadataService();
    //7. 将本身添加到事件监听器列表中,因为DubboBootstrap本身就是一个事件监听器
    initEventListener();

    if (logger.isInfoEnabled()) {
        logger.info(NAME + " has been initialized!");
    }
}

初始化过程图解

image.png

第一步初始化框架拓展

FrameworkExt 继承了 LifeCycle。这里通过SPI机制加载FrameworkExt 类扩展实例,并调用实例的初始化方法

public static void initFrameworkExts() {
    Set<FrameworkExt> exts = ExtensionLoader.getExtensionLoader(FrameworkExt.class).getSupportedExtensionInstances();
    for (FrameworkExt ext : exts) {
        ext.initialize();
    }
}

FrameworkExt 共有三个实现 ConfgiManager、Environment、ServiceRepository

image.png

其中 Environment 重写了 initialize方法。将配置中心信息存入ApplicationModel本地缓存中。

//就是将配置中心COnfigCenter的信息存入到本地缓存中
@Override
public void initialize() throws IllegalStateException {
    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);
}

第二步 初始化配置中心

private void startConfigCenter() {

    // 当注册中心使用zookeeper时,如果配置中心地址没有指定就以注册中心zookeeper作为默认的配置中心
    useRegistryAsConfigCenterIfNecessary();

    //从ConfigManager实例中获取配置中心的配置
    Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();

    // check Config Center
    if (CollectionUtils.isEmpty(configCenters)) {
        ConfigCenterConfig configCenterConfig = new 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对象实例中
        environment.setDynamicConfiguration(compositeDynamicConfiguration);
    }
    //刷新配置
    configManager.refreshAll();
}

使用zookeeper作为注册中心时,没有指定配置中心地址,默认使用zookeeper 作为配置中心

private void useRegistryAsConfigCenterIfNecessary() {
    // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
    //配置了注册中心
    if (environment.getDynamicConfiguration().isPresent()) {
        return;
    }

    //配置中心没有配置,如果有就直接返回
    if (CollectionUtils.isNotEmpty(configManager.getConfigCenters())) {
        return;
    }

    //从configManager中获取注册中心的地址,然后利用注册中心的地址信息构建ConfigCenterConfig
    configManager
            .getDefaultRegistries()
            .stream()
            .filter(this::isUsedRegistryAsConfigCenter)
            .map(this::registryAsConfigCenter)
            .forEach(
                    configManager::addConfigCenter ////将新建的ConfigCenterConfig添加到ConfigManager对象中
            );
}

第三步加载远程配置

处理RegistryConfig和ProtocolConfig的 ID不一致问题

private void loadRemoteConfigs() {
    // registry ids to registry configs
    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;
            }));
        }
    });

    configManager.addRegistries(tmpRegistries);

    // protocol ids to protocol configs
    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.refresh();
                return protocolConfig;
            }));
        }
    });

    configManager.addProtocols(tmpProtocols);
}

第四步 检查全局配置

主要对各个组件进行非空和命名规范检查

private void checkGlobalConfigs() {
    // 检查Application
    ConfigValidationUtils.validateApplicationConfig(getApplication());

    //校验元数据
    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);
        }
    }

    // 校验服务提供者配置
    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);
    }
    // 校验服务消费者配置
    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);
    }

    //校验监控配置
    ConfigValidationUtils.validateMonitorConfig(getMonitor());
    // check Metrics
    ConfigValidationUtils.validateMetricsConfig(getMetrics());
    // check Module
    ConfigValidationUtils.validateModuleConfig(getModule());
    // check Ssl
    ConfigValidationUtils.validateSslConfig(getSsl());
}

第五步 初始化元数据

private void initMetadataService() {
    startMetadataCenter();
    this.metadataService = getDefaultExtension();
    this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
}

第六步 将自身加入事件监听队列

private void initEventListener() {
    // Add current instance into listeners
    addEventListener(this);
}
public DubboBootstrap addEventListener(EventListener<?> listener) {
    eventDispatcher.addEventListener(listener);
    return this;
}

DubboBootStrap 服务暴露

将服务注册到注册中心,如果消费者订阅了注册中心存储的服务,提供对应服务接口的远程调用

private void exportServices() {
    //从配置管理器中获取到所有的要暴露的服务配置,一个接口类对应一个ServiceConfigBase实例
    //从ConfigManager中获取所有的ServiceConfig实例,然后遍历,一个一个的暴露
    configManager.getServices().forEach(sc -> {
        // TODO, compatible with ServiceConfig.export()
        // 服务export的核心逻辑方法是ServiceConfig#export()
        ServiceConfig serviceConfig = (ServiceConfig) sc;
        //将DubboBootstrap实例赋值给ServiceConfig
        serviceConfig.setBootstrap(this);
        //判断是否异步暴露接口
        if (exportAsync) {
            //如果是异步暴露接口,就获取一个线程池来异步执行接口暴露逻辑
            ExecutorService executor = executorRepository.getServiceExporterExecutor();
            Future<?> future = executor.submit(() -> {
                //暴露接口,将接口信息注册到注册中心
                sc.export();
                exportedServices.add(sc);
            });
            asyncExportingFutures.add(future);
        } else {
            //同步export
            sc.export();
            exportedServices.add(sc);
        }
    });
}
public synchronized void export() {

    //如果bootstrap为空即没有初始化,就初始化
    if (bootstrap == null) {
        bootstrap = DubboBootstrap.getInstance();
        bootstrap.initialize();
    }

    checkAndUpdateSubConfigs();

    // 初始化服务的原数据信息
    //init serviceMetadata
    serviceMetadata.setVersion(getVersion());
    serviceMetadata.setGroup(getGroup());
    serviceMetadata.setDefaultGroup(getGroup());
    serviceMetadata.setServiceType(getInterfaceClass());
    //设置服务接口
    serviceMetadata.setServiceInterfaceName(getInterface());
    //设置服务实现类
    serviceMetadata.setTarget(getRef());

    //如果服务已经export那么就返回
    if (!shouldExport()) {
        return;
    }

    //是否为延迟export
    if (shouldDelay()) {
        //如果是延时export,就使用时间调度器延时服务export逻辑
        DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
    } else {
        //不是延时export那就直接export
        doExport();
    }

    //发布serviceConfigExportedEvent事件
    exported();
}

doExprot 准备执行服务暴露

protected synchronized void doExport() {
    //如果service已经被unexported,就抛出异常
    if (unexported) {
        throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
    }
    //如果服务已经被暴露,就返回
    if (exported) {
        return;
    }
    exported = true;

    //设置path值,值为接口的全限定名
    if (StringUtils.isEmpty(path)) {
        path = interfaceName;
    }
    //执行服务暴露逻辑
    doExportUrls();
}

核心暴露方法,一个服务配置实例 ServiceConfig 可能同时以多个协议暴露

@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
    //从ApplicationModel容器中获取服务仓库ServiceRepository(也是一个容器)
    ServiceRepository repository = ApplicationModel.getServiceRepository();
    //生产一个服务描述实例ServiceDescriptor
    ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
    //将服务提供者的信息存放到ServiceRepository中
    repository.registerProvider(
            getUniqueServiceName(),
            ref,
            serviceDescriptor,
            this,
            serviceMetadata
    );

    //获取服务注册中心的URL地址信息,一个服务可以同时在多个注册中心注册。所以此处是List
    List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

    //遍历服务要暴露的协议集合。一个服务可能同时以dubbo协议,rest协议等export。一般默认是dubbo协议,也推荐是用dubbo协议
    for (ProtocolConfig protocolConfig : protocols) {
        //构建服务的path的key
        String pathKey = URL.buildKey(getContextPath(protocolConfig)
                .map(p -> p + "/" + path)
                .orElse(path), group, version);
        repository.registerService(pathKey, interfaceClass);
        serviceMetadata.setServiceKey(pathKey);
        //针对一个协议,在一个或多个注册中心来export这个服务
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

针对不同协议,在一个或多个注册中心暴露此服务。此方法主要:根据协议组装参数,根据scope来决定以本地(local)方式还是以远程(remote)方式暴露服务. 关键逻辑在于使用ProxyFactory来生成代理接口invoker,并且生成invoker包装类即扩展了ServiceConfig的Invoker对象,根据Protocol把invoker包装类 进行暴露 之后放入exporters 已暴露的服务集合中

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    //获取协议名称,没有则默认使用dubbo
    String name = protocolConfig.getName();
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }

    // 组装参数 一直到461行
    Map<String, String> map = new HashMap<String, String>();
    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())) {
        for (MethodConfig method : getMethods()) {
            AbstractConfig.appendParameters(map, method, method.getName());
            String retryKey = method.getName() + ".retry";
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                if ("false".equals(retryValue)) {
                    map.put(method.getName() + ".retries", "0");
                }
            }
            List<ArgumentConfig> arguments = method.getArguments();
            if (CollectionUtils.isNotEmpty(arguments)) {
                for (ArgumentConfig argument : arguments) {
                    // convert argument 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();
                                // target the method, and get its signature
                                if (methodName.equals(method.getName())) {
                                    Class<?>[] argtypes = methods[i].getParameterTypes();
                                    // one callback in the method
                                    if (argument.getIndex() != -1) {
                                        if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                            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];
                                            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());
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else if (argument.getIndex() != -1) {
                        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
    }

    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();
        if (methods.length == 0) {
            logger.warn("No method found in service interface " + interfaceClass.getName());
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }

    /**
     * Here the token value configured by the provider is used to assign the value to ServiceConfig#token
     */
    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);
        }
    }
    //init serviceMetadata attachments, ServiceMetadata中的attachments will be transferred to remote side
    serviceMetadata.getAttachments().putAll(map);

    // export service , 构建URL实例
    String host = findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = findConfigedPorts(protocolConfig, name, map);
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

    // You can customize Configurator to append extra parameters
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    //获取scope ,如果没有配置服务的scope时就不暴露服务
    String scope = url.getParameter(SCOPE_KEY);
    // don't export when none is configured
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

        // export to local if the config is not remote (export to remote only when config is remote)
        //如果scope != remote , 就以本地方式export
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            exportLocal(url);
        }
        // export to remote if the config is not local (export to local only when config is local)
        //如果scope不是local,就默认以remote方式export
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            if (CollectionUtils.isNotEmpty(registryURLs)) {
                //遍历注册中心的地址。来一个一个的export
                for (URL registryURL : registryURLs) {
                    //if protocol is only injvm ,not register 如果协议为injvm, 就不需要暴露,在同一个jvm内的服务
                    if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                        continue;
                    }
                    //添加dynamic参数配置
                    url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));

                    //添加监控中心url配置, 可选
                    URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
                    if (monitorUrl != null) {
                        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);
                    }

                    // 使用ProxyFactory来生成接口的代理。invoker是一个执行者
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                    //生成Invoker的包装类,实现了Invoker接口,其本身也是一个Invoker,只是在invoker的基础上扩展了ServiceConfig
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    //使用Protocol来暴露服务接口到对应的注册中心
                    //    private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
                    //取的是Protocol 接口的代理类的实例。SPI的时候讲过了执行代理类中的方法的时候,是从url中取参数的。
                    Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                    //将这个Exporter实例存入ServiceConfig中的exporters属性中
                    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);
            }

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

PROTOCOL.export(wrapperInvoker),默认协议是dubbo。因此调用的是 DubboProtocol.export函数。 做了两件事情: ①开启服务 ②注册到注册中心

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

    String key = serviceKey(url);
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    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;
}

image.png