Dubbo-服务提供者启动流程

参考:https://blog.csdn.net/prestigeding/article/details/80536385

设计图

ServiceBean类结构图

InitializingBean:其声明的接口为afterPropertiesSet方法,顾名思义,就是在bean初始化所有属性之后调用。
DisposableBean:其声明的接口为destroy()方法,在Spring BeanFactory销毁一个单例实例之前调用。
ApplicationContextAware:其声明的接口为void setApplicationContext(ApplicationContext applicationContext),实现了该接口,Spring容器在初始化Bean时会调用该方法,注入ApplicationContext:已方便该实例可以直接调用applicationContext获取其他Bean。
ApplicationListener< ContextRefreshedEvent>:容器重新刷新时执行事件函数。
BeanNameAware:其声明的接口为:void setBeanName(String name),实现该接口的Bean,其实例可以获取该实例在BeanFactory的id或name。
FactoryBean:Spring初始化Bean的另外一种方式,例如dubbo:reference,需要返回的对象并不是ReferenceBean,而是要返回ref指定的代理类来执行业务操作,故这里使用FactoryBean非常合适,FactoryBean定义了如下三个方法:
  T getObject() throws Exception:获取需要返回的结果对象。
  Class<?> getObjectType():获取返回对象的类型。
  boolean isSingleton():返回是否是单例。  

Spring事件监听

  DubboBootstrapApplicationListener接收Spring容器刷新完成事件

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
        implements Ordered {

    /**
     * The bean name of {@link DubboBootstrapApplicationListener}
     *
     * @since 2.7.6
     */
    public static final String BEAN_NAME = "dubboBootstrapApplicationListener";

    private final DubboBootstrap dubboBootstrap;

    public DubboBootstrapApplicationListener() {
        this.dubboBootstrap = DubboBootstrap.getInstance();
    }

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

    private void onContextClosedEvent(ContextClosedEvent event) {
        dubboBootstrap.stop();
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}

  DubboBootstrap#start

/**
 * Start the bootstrap
 */
public DubboBootstrap start() {
	if (started.compareAndSet(false, true)) {
		ready.set(false);
		// 读取配置信息
		initialize();
		if (logger.isInfoEnabled()) {
			logger.info(NAME + " is starting...");
		}
		// 1. export Dubbo Services
		// 发布Dubbo服务
		exportServices();

		// Not only provider register
		if (!isOnlyRegisterProvider() || hasExportedServices()) {
			// 2. export MetadataService
			exportMetadataService();
			//3. Register the local ServiceInstance if required
			registerServiceInstance();
		}

		referServices();
		if (asyncExportingFutures.size() > 0) {
			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;
}

private void exportServices() {
	configManager.getServices().forEach(sc -> {
		// TODO, compatible with ServiceConfig.export()
		ServiceConfig serviceConfig = (ServiceConfig) sc;
		serviceConfig.setBootstrap(this);

		// 异步发布
		if (exportAsync) {
			ExecutorService executor = executorRepository.getServiceExporterExecutor();
			Future<?> future = executor.submit(() -> {
				sc.export();
				exportedServices.add(sc);
			});
			asyncExportingFutures.add(future);
		} 
		// 同步发布
		else {
			sc.export();
			exportedServices.add(sc);
		}
	});
}

  ServiceConfig#export

public synchronized void export() {
	// 是否需要发布,由<dubbo:service export="true|false" />来指定
	// 未指定由<dubbo:provider export="true|false" />来指定
	if (!shouldExport()) {
		return;
	}

	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());

	// 延迟发布
	if (shouldDelay()) {
		// 创建并执行在给定延迟后启用的一次性操作
		DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
	} 
	// 及时发布
	else {
		doExport();
	}

	exported();
}

  ServiceConfig#checkAndUpdateSubConfigs

private void checkAndUpdateSubConfigs() {
	// 如果为<dubbo:service />|<dubbo:reference />设置了全局配置,
	// 如<dubbo:provider />|<dubbo:consumer />,则缺省为全局配置
	completeCompoundConfigs();
	// 没有配置<dubbo:provider />,设置默认ProviderConfig对象
	checkDefault();
	// 没有配置<dubbo:protocol />,设置默认protocol,或者将protocolIds转化为ProtocolConfig对象
	checkProtocol();
	// SPI方式初始化一些配置(ReferenceConfig|ServiceConfig)
	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则设置默认注册配置或者将registryIds转化为RegistryConfig对象
	// 校验注册信息的合法性,如address是否为空
	if (!isOnlyInJvm()) {
		checkRegistry();
	}
	// 刷新配置,一些未配置的信息使用默认配置
	this.refresh();

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

	// 泛型调用服务
	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);
		}
		// 校验接口和方法合法性
		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);
		}
	}
	// 本地存根合法性校验,如是否有有参构造器
	checkStubAndLocal(interfaceClass);
	// 校验mock合法性,如mock类是否实现了interfaceClass
	// false,不调用mock服务。
	// true,当服务调用失败时,调用mock服务。
	// default,当服务调用失败时,调用mock服务。
	// force,强制调用mock服务(不管服务能否调用成功)。
	ConfigValidationUtils.checkMock(interfaceClass, this);
	// 校验配置参数值合法性
	ConfigValidationUtils.validateServiceConfig(this);
	// SPI方式配置信息校验成功后的后置处理
	postProcessConfig();
}

  ServiceConfig#doExportUrls

private void doExportUrls() {
	ServiceRepository repository = ApplicationModel.getServiceRepository();
	// 注册服务描述映射接口全类名
	ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
	// ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息。
	// 比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel。
	// ApplicationModel 持有所有的 ProviderModel。
	repository.registerProvider(
			getUniqueServiceName(),
			ref,
			serviceDescriptor,
			this,
			serviceMetadata
	);

	/**
	 * 解析获得注册中心URL集合
	 * 
	 * 1.同类型注册中心
	 * 		<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181;192.168.88.128:2181" />
	 * 	解析获得两条数据:
	 * 		registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3060&qos.port=22226&registry=zookeeper&timestamp=1610074712005
	 *		registry://192.168.88.128:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3060&qos.port=22226&registry=zookeeper&timestamp=1610074712005
	 * 2.同类型注册中心
	 * 		<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181,192.168.88.128:2181" />
	 * 	解析获得一条数据:
	 * 		registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&backup=192.168.88.128:2181&dubbo=2.0.2&pid=10212&qos.port=22226&registry=zookeeper&timestamp=1610074806198
	 * 3.不同类型注册中心
	 * 		<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" />
	 *		<dubbo:registry protocol="redis" address="127.0.0.1:8848" />
	 * 	解析获得两条数据:
	 * 		registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=10196&qos.port=22226&registry=zookeeper&timestamp=1610074944556
	 *		registry://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=10196&qos.port=22226&registry=redis&timestamp=1610074944567
	 */
	List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

	for (ProtocolConfig protocolConfig : protocols) {
		/**
		 * 如果设置了contextpath参数,设置方式有两种:
		 * 	<dubbo:protocol contextpath="demoProvider" name="dubbo" port="20881" />
		 * 	<dubbo:provider contextpath="demoProvider" />
		 * 未设置contextpath参数,pathKey=demoProviderGroup/org.apache.dubbo.demo.DemoService/v1.0
		 * 设置了contextpath参数,pathKey=demoProviderGroup/demoProvider/org.apache.dubbo.demo.DemoService/v1.0
		 */
		String pathKey = URL.buildKey(getContextPath(protocolConfig)
				.map(p -> p + "/" + path)
				.orElse(path), group, version);
		// 如果指定了路径,则再次注册服务描述映射路径,<pathKey, ServiceDescriptor>
		repository.registerService(pathKey, interfaceClass);
		serviceMetadata.setServiceKey(pathKey);
		// 指定协议注册发布服务
		doExportUrlsFor1Protocol(protocolConfig, registryURLs);
	}
}

  ConfigValidationUtils#loadRegistries

public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
    // check && override if necessary
    List<URL> registryList = new ArrayList<URL>();
    ApplicationConfig application = interfaceConfig.getApplication();
    List<RegistryConfig> registries = interfaceConfig.getRegistries();
    if (CollectionUtils.isNotEmpty(registries)) {
        for (RegistryConfig config : registries) {
            String address = config.getAddress();
            if (StringUtils.isEmpty(address)) {
                // 若 address 为空,则将其设为 0.0.0.0
                address = ANYHOST_VALUE;
            }
            // 如果address不是N/A,说明服务需要远程注册到注册中心
            if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                Map<String, String> map = new HashMap<String, String>();
                // 将ApplicationConfig、RegistryConfig中的属性值添加到map中
                AbstractConfig.appendParameters(map, application);
                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);
                }
                /**
                 * 1. <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181;192.168.88.128:2181" />
                 *  解析获得两条数据:
                 *  zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=7220&qos.port=22226&timestamp=1610073722877
                 *  zookeeper://192.168.88.128:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=7220&qos.port=22226&timestamp=1610073722877
                 * 2. <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181,192.168.88.128:2181" />
                 *  解析获得一条数据:
                 *  zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&backup=192.168.88.128:2181&dubbo=2.0.2&pid=3164&qos.port=22226&timestamp=1610073611665
                 */
                List<URL> urls = UrlUtils.parseURLs(address, map);
 
                for (URL url : urls) {
                    url = URLBuilder.from(url)
                            // 添加&registry=zookeeper参数信息
                            .addParameter(REGISTRY_KEY, url.getProtocol())
                            // 将 URL协议头设置为 registry
                            // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3128&qos.port=22226&registry=zookeeper&timestamp=1610073611665
                            // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3128&qos.port=22226&registry=zookeeper&timestamp=1610073611665
                            .setProtocol(extractRegistryType(url))
                            .build();
                    // 通过判断条件,决定是否添加 url 到 registryList:
                    // 如果是服务提供者且配置了register=false,则忽略该地址
                    // 如果是服务消费者且配置了subscribe=false则忽略该地址
                    if ((provider && url.getParameter(REGISTER_KEY, true))
                            || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
                        registryList.add(url);
                    }
                }
            }
        }
    }
    return registryList;
}

  ServiceConfig#doExportUrlsFor1Protocol

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

	// 添加 side、版本、时间戳以及进程号等信息到 map 中
	Map<String, String> map = new HashMap<String, String>();
	map.put(SIDE_KEY, PROVIDER_SIDE);

	// 添加dubbo版本号、timestamp、pid等信息到 map 中
	ServiceConfig.appendRuntimeParameters(map);
	// 通过反射将MetricsConfig、ApplicationConfig、ModuleConfig、ProviderConfig、
	// ProtocolConfig、ServiceConfig中的属性值添加到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) {
					// convert argument type 检测 type 属性是否为空,或者空串(分支1 ⭐️)
					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) {
										// 检测 ArgumentConfig 中的 type 属性与方法参数列表
										// 中的参数名称是否一致,不一致则抛出异常(分支2 ⭐️)
										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 { // 分支3 ⭐️
										// 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) { // 分支4 ⭐️
						// 添加 ArgumentConfig 字段信息到 map 中 <dubbo:argument index='0' callback="true" />
						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配置方式有两种:
	// 	<dubbo:service token="true" />
	// 	<dubbo:provider token="true" />
	if(ConfigUtils.isEmpty(token) && provider != null) {
		token = provider.getToken();
	}

	// 添加 token 到 map 中
	// 消费者提供了与提供者一致的token,才能访问提供者提供的服务
	if (!ConfigUtils.isEmpty(token)) {
		// 如果是true或者是default则使用UUID
		if (ConfigUtils.isDefault(token)) {
			map.put(TOKEN_KEY, UUID.randomUUID().toString());
		} else {
			map.put(TOKEN_KEY, token);
		}
	}
	//init serviceMetadata attachments
	serviceMetadata.getAttachments().putAll(map);

	// 解析服务提供者的IP地址与端口
	String host = findConfigedHosts(protocolConfig, registryURLs, map);
	Integer port = findConfigedPorts(protocolConfig, name, map);
	// 组装 URL,获取上下文路径
	// dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&pid=1368&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool&timestamp=1610093664272
	URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

	// SPI方式自定义ConfiguratorFactory、Configurator对象添加额外的参数
	if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
			.hasExtension(url.getProtocol())) {
		// 加载 ConfiguratorFactory,并生成 Configurator 实例,然后通过实例配置 url
		url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
				.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
	}

	/**
	 * 一个服务可能既是Provider又是Consumer,因此就存在他自己调用自己服务的情况
	 * 如果再通过网络去访问,那自然是舍近求远,因此他是有本地暴露服务的这个设计
	 *
	 * 本地暴露是暴露在JVM中,不需要网络通信
	 * 远程暴露是将ip,端口等信息暴露给远程客户端,调用时需要网络通信
	 */
	String scope = url.getParameter(SCOPE_KEY);
	// 如果配置为 none 不暴露服务,不向注册中心注册服务信息
	// 不配置scope时,既暴露服务也考虑向注册中心注册服务信息
	if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

		// 当配置不是 remote 时则进行本地服务暴露 (当配置为 remote 时只进行远程服务暴露)
		if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
			// 本地(injvm)服务暴露,在本地JVM内存中创建存储Exporter对象
			exportLocal(url);
		}
		// 当配置不是 local 时进行远程服务暴露 (当配置为 local 时只进行本地服务暴露)
		if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
			// 注册中心地址存在时,向注册中心注册服务信息
			if (CollectionUtils.isNotEmpty(registryURLs)) {
				for (URL registryURL : registryURLs) {
					// 如果配置<dubbo:registry protocol="injvm" address="127.0.0.1:0" />时
					// 只暴露本地服务,不向注册中心注册服务信息
					if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
						continue;
					}
					// 如果dubbo:service的dynamic属性未配置, 尝试取dubbo:registry的dynamic属性,
					// 该属性的作用是否启用动态注册,如果设置为false,服务注册后,其状态显示为disable,
					// 需要人工启用,当服务不可用时,也不会自动移除,同样需要人工处理,此属性不要在生产环境上配置。
					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(dubbo的远程调用实现类)
					Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
					// DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
					DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

					// 导出服务,并生成 Exporter
					// NettyServer监听IP为bind.ip(192.168.88.2),端口为bind.port(20881)
					Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
					exporters.add(exporter);
				}
			} 
			// 当配置<dubbo:registry protocol="zookeeper" address="N/A" />时
			// 不存在注册中心地址,仅暴露服务
			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);
}

  RegistryProtocol#export

/**
 * 1.调用 doLocalExport 暴露服务
 * 2.向注册中心注册服务,为了消费端获取可用服务
 * 3.向注册中心进行订阅,为了服务提供者URL发送变化后重新暴露服务
 * 4.创建并返回 DestroyableExporter
 */
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // 获取注册中心 URL,以 zookeeper 注册中心为例:registry:// -> zookeeper://
    // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.88.2%3A20881%2Fdemo-provider%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D192.168.88.2%26bind.port%3D20881%26deprecated%3Dfalse%26dispatcher%3DiDispatcher%26dubbo%3D2.0.2%26dynamic%3Dtrue%26executes%3D2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26loadbalance%3Dleastactive%26methods%3DsayHello%2CsayHelloAsync%26module%3Ddemo-module%26monitor%3Ddubbo%253A%252F%252F127.0.0.1%253A2181%252Forg.apache.dubbo.registry.RegistryService%253Fapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526pid%253D1444%2526protocol%253Dregistry%2526qos.port%253D22226%2526refer%253Dapplication%25253Ddemo-provider%252526dubbo%25253D2.0.2%252526interface%25253Dorg.apache.dubbo.monitor.MonitorService%252526interval%25253D100%252526pid%25253D1444%252526qos.port%25253D22226%252526register.ip%25253D192.168.88.2%252526timestamp%25253D1610333468481%2526registry%253Dzookeeper%2526timestamp%253D1610333468168%26pid%3D1444%26qos.port%3D22226%26release%3D%26sayHello.retries%3D2%26side%3Dprovider%26threadpool%3DiThreadPool%26timestamp%3D1610333468176&pid=1444&qos.port=22226&timestamp=1610333468168
    URL registryUrl = getRegistryUrl(originInvoker);
    // 获取URL的export属性值即服务提供者的URL
    // dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D1444%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D1444%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610333468481%26registry%3Dzookeeper%26timestamp%3D1610333468168&pid=1444&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool&timestamp=1610333468176
    URL providerUrl = getProviderUrl(originInvoker);
 
    // 获取订阅 URL,服务提供者向注册中心订阅自己,主要是为了服务提供者URL发送变化后重新暴露服务,当然会将dubbo:reference的check属性设置为false
    // provider://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&category=configurators&check=false&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D1444%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D1444%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610333468481%26registry%3Dzookeeper%26timestamp%3D1610333468168&pid=1444&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool&timestamp=1610333468176
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    // 创建监听器,当配置中心数据发生变化时进行监听处理
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
 
    // 根据外部配置刷新本地配置(如果外部配置和本地配置信息不一致的话)
    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    // 暴露远程服务并加缓存,根据各自协议,服务提供者建立网络服务器在特定端口建立监听,监听来自消息消费端服务的请求
    // 以dubbo协议为例,创建NettyServer监听指定IP和端口
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
 
    // 根据注册中心URL,从注册中心工厂中获取指定的注册中心实现类:zookeeper注册中心的实现类为:ZookeeperRegistry
    // ZookeeperRegistry的构造器中连接注册中心
    final Registry registry = getRegistry(originInvoker);
    // 获取服务提供者URL中的register属性
    // 如果为true则调用注册中心的ZookeeperRegistry#register方法向注册中心注册服务(实际由其父类FailbackRegistry实现)
    final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
 
    // 根据 register 属性的值决定是否注册服务,默认为true
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        // 向注册中心注册服务
        // 以zookeeper为例将服务信息存储到相应目录中
        register(registryUrl, registeredProviderUrl);
    }
 
    // register stated url on provider model
    registerStatedUrl(registryUrl, registeredProviderUrl, register);
 
    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
 
    // 服务提供端向注册中心进行订阅,即将弃用,这种方式使用于2.6.x版本或更早版本
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
 
    // 服务注册暴露成功后进行通知
    notifyExport(exporter);
    // 创建并返回 DestroyableExporter
    return new DestroyableExporter<>(exporter);
}

  RegistryProtocol#doLocalExport

@SuppressWarnings("unchecked")
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
	String key = getCacheKey(originInvoker);
	// 访问缓存,不存在则写缓存
	return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
		// 创建 Invoker 委托类对象
		// providerUrl=dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D1444%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D1444%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610333468481%26registry%3Dzookeeper%26timestamp%3D1610333468168&pid=1444&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool&timestamp=1610333468176
		Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
		// 根据Dubbo内置的SPI机制,将调用DubboProtocol#export方法
		return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
	});
}

  DubboProtocol#export

@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
	// dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D11080%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D11080%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610336987787%26registry%3Dzookeeper%26timestamp%3D1610336987412&pid=11080&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool&timestamp=1610336987422
	URL url = invoker.getUrl();

	// 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如:
	// demoGroup/org.apache.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;
}

  DubboProtocol#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重置服务器
			// 因为一个dubbo应用服务中会有多个<dubbo:service />标签
			// 这些标签都会在服务提供者的同一个IP地址、端口号上暴露服务
			server.reset(url);
		}
	}
}

  DubboProtocol#createServer

/**
 * 1.检测是否存在 server 参数所代表的 Transporter 拓展
 * 2.创建服务器实例
 * 3.检测是否支持 client 参数所表示的 Transporter 拓展,不存在也是抛出异常
 */
private ProtocolServer createServer(URL url) {
	url = URLBuilder.from(url)
			// 为服务提供者url增加channel.readonly.sent属性默认为true,表示在发送请求时是否等待将字节写入socket后再返回
			.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
			// 为服务提供者url增加heartbeat属性,表示心跳间隔时间,默认为60*1000,表示60s
			.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
			// 为服务提供者url增加codec属性,默认值为dubbo,协议编码方式
			.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 {
		// 根据服务提供者URL,ExchangeHandler构建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);
}

  Exchangers#bind

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

@Override
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)))
	// 	 4. new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))))
	return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}

  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 实例,并调用bind方法
	return getTransporter().bind(url, handler);
}

  Transporter继承结构图

 

   以NettyTransporter为例,NettyTransporter#bind

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

  NettyServer#doOpen

@Override
protected void doOpen() throws Throwable {
	NettyHelper.setNettyLoggerFactory();
	// 创建 boss 和 worker 线程池
	ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
	ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
	ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
	// 创建 ServerBootstrap
	bootstrap = new ServerBootstrap(channelFactory);

	final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
	channels = nettyHandler.getChannels();
	// https://issues.jboss.org/browse/NETTY-365
	// https://issues.jboss.org/browse/NETTY-379
	// final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
	bootstrap.setOption("child.tcpNoDelay", true);
	bootstrap.setOption("backlog", getUrl().getPositiveParameter(BACKLOG_KEY, Constants.DEFAULT_BACKLOG));
	// 设置 PipelineFactory
	bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
		@Override
		public ChannelPipeline getPipeline() {
			NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
			ChannelPipeline pipeline = Channels.pipeline();
			/*int idleTimeout = getIdleTimeout();
			if (idleTimeout > 10000) {
				pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
			}*/
			pipeline.addLast("decoder", adapter.getDecoder());
			pipeline.addLast("encoder", adapter.getEncoder());
			pipeline.addLast("handler", nettyHandler);
			return pipeline;
		}
	});
	// bind 绑定到指定的 ip 和端口上
	channel = bootstrap.bind(getBindAddress());
}

  从DubboProtocol#openServer中可以看出,解析第一个<dubbo:service/>标签时会调用DubboProtocol#createServer方法,在解析后续的<dubbo:service/>标签时并不会调用DubboProtocol#createServer方法,而是会调用DubboProtocolServer#reset方法即调用NettyServer#reset方法

  AbstractServer#reset

@Override
public void reset(URL url) {
	if (url == null) {
		return;
	}
	try {
		if (url.hasParameter(ACCEPTS_KEY)) {
			int a = url.getParameter(ACCEPTS_KEY, 0);
			if (a > 0) {
				this.accepts = a;
			}
		}
	} catch (Throwable t) {
		logger.error(t.getMessage(), t);
	}
	try {
		if (url.hasParameter(IDLE_TIMEOUT_KEY)) {
			int t = url.getParameter(IDLE_TIMEOUT_KEY, 0);
			if (t > 0) {
				this.idleTimeout = t;
			}
		}
	} catch (Throwable t) {
		logger.error(t.getMessage(), t);
	}
	// 调整线程池的相关线程数量?
	// 多个<dubbo:service />标签对应一个executor,线程池core数量和max数量怎么确定?
	executorRepository.updateThreadpool(url, executor);
	// 覆盖原先NettyServer的volatile URL url的属性,NettyHandler上加了注解@Sharable?
	super.setUrl(getUrl().addParameters(url.getParameters()));
}

  

posted @ 2021-01-06 17:46  BINGJJFLY  阅读(169)  评论(0编辑  收藏  举报