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®istry=zookeeper×tamp=1610074712005 * registry://192.168.88.128:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3060&qos.port=22226®istry=zookeeper×tamp=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®istry=zookeeper×tamp=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®istry=zookeeper×tamp=1610074944556 * registry://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=10196&qos.port=22226®istry=redis×tamp=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×tamp=1610073722877 * zookeeper://192.168.88.128:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=7220&qos.port=22226×tamp=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×tamp=1610073611665 */ List<URL> urls = UrlUtils.parseURLs(address, map); for (URL url : urls) { url = URLBuilder.from(url) // 添加®istry=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®istry=zookeeper×tamp=1610073611665 // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3128&qos.port=22226®istry=zookeeper×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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())); }