Dubbo Learning Records (10) --- Processing Registry Configuration of Service Export and Talking about the Use of Constructor Mode

service export

Dubbo service export general process

  1. Each Service annotation of Dubbo corresponds to a ServiceBean object. Each ServiceBean object implements the Spring ApplicationListener interface. When the Spring project is started, a context refresh event ContextRefreshEvent event will be triggered. The trigger method is the onApplicationEvent method, ServiceBean onApplicationEvent In the method, ServiceBean#export() will be called, and then the parent class ServiceConfig#export() method will be called to export the service
  2. It will determine the configuration parameters of each ServiceBean, such as timeout, protocol, etc. The configuration of these parameters can be configured in the following places;
    2.1 JVM environment operating parameters, specify parameters by -D
    2.2 The configuration center can configure the global configuration of the project And application configuration, which is equivalent to a distributed shared Dubbo.properties file;
    2.3 @Service annotation can specify parameters;
    2.4 In the project, the specified dubbo.properties configuration file;
    so many places can specify configuration parameters, when the service is exported, It is necessary to determine which configuration parameters are used by each Service, that is, to determine the parameters with the highest priority;
  3. Load and determine the registration center information;
  4. Start the container according to the configured Protocol, the Dubbo protocol corresponds to Netty, http corresponds to tomcat/jetty, and in the process of receiving requests, receive requests according to our configuration, such as timeout events, maximum number of connections, etc.;
  5. After the container is started, the service configuration of each ServiceBean will be registered to the registration center, and at the same time, it will register to monitor the service configuration and monitor the configuration changes; these are the main
    processes. It is easier to determine the service configuration parameters => start the container => register to the Registration Center;
    public synchronized void export() {
    
    
    	//确定服务参数
        checkAndUpdateSubConfigs();

        // 检查服务是否需要导出
        if (!shouldExport()) {
    
    
            return;
        }

        // 检查是否需要延迟发布
        if (shouldDelay()) {
    
    
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
    
    
            // 导出服务
            doExport();
        }
    }

Service Export Ideas

There are several things to be done for service export:

  1. Determine the parameters of the service
  2. Determine the protocols supported by the service
  3. Construct the final URL of the service
  4. According to different protocols supported by the service, start different Servers to receive and process requests
  5. Register the service URL to the registration center
  6. Because Dubbo supports dynamic configuration of service parameters, it is necessary to bind a Listener to monitor whether the parameters of the service have been modified when the service is exported. If any modification is found, the export needs to be re-exported.

Determine the parameters of the service

I wrote a separate article in the previous blog, so I won’t repeat the description.
A brief overview of the process:

  1. Complete parameters;
  2. Refresh the configuration parameters;
    2.1 Obtain the configuration parameters of the configuration center, such as the address of the configuration center; and refresh the parameters of the configuration center
    2.2 Obtain the global configuration and application configuration of the configuration center, and put them into the two Map attributes of Environment's appExternalConfiguration and externalConfiguration;
    2.3 Refresh all Parameters of Dubbo configuration class; ==>ApplicationConfig, MonitorConfig, ModuleConfig, ProtocolConfig, RegistryConfig, ProviderConfig, ConsumerConfig
    (Note: In the process of refreshing parameters, specify the priority level of service parameters, JVM environment configuration > configuration center App application configuration > configuration center Global configuration > annotation parameter configuration > dubbo.properties file configuration)
  3. Create a setting provider configuration class, managed by ConfigManager
  4. Create and set the Protocol configuration class, managed by Configmanager
  5. Create and set Application, managed by ConfigManager;
  6. The refresh of the ServiceConfig parameter represented by the @Service annotation, the configuration parameter values ​​set at this time are all of the highest priority.
  7. Some configurations of Mock, Stub, etc.;

Handle registry configuration

A project may configure multiple registries, and Dubbo will convert these registry configurations into individual RegistryConfigs. In the stage of determining service configuration parameters, each RegistryConfig obtains the highest priority parameter;
purpose: to process RegistryConfig, parse each registry configuration into individual URLs, and finally return a URL set;

private void doExportUrls() {
    
    
        // 得到url,注册服务也是一个服务,所以也会有对应的url,通过调用该url完成服务注册
        List<URL> registryURLs = loadRegistries(true);   //
		
		//...后面的也是重点代码,但与注册中心处理无关;
    }

loadRegistries(true)

Work:

  1. Traverse the registry configuration class;
  2. If the registration center configuration class does not set the address address, the anonymous address 0.0.0.0 will be used
  3. If the address of the registration center configuration class is "N/A", jump out of the loop and execute the next configuration class;
  4. If the address of the registry configuration class is not "N/A", then
    4.1 get the parameters of ApplicationConfig and put them into the map; (by traversing the Method method)
    4.2 get the parameters of RegistryConfig and put them into the map; (by traversing the Method method)
    register The central configuration class packs the user's name username and password into the map;
    4.3 Add the value of path and fix it as RegistryService.class.getName();
    4.4 Obtain the Dubbo version information and put it into the map;
    4.5 Construct the URL and pass Address + parameter (UrlUtils.parseURLs(address, map));
    4.6 Pass in the value of the protocol parameter and the protocol name of the URL to build the final URL;
    4.7 Obtain the value of the provider’s register from the URL, true to register, false otherwise; consumer , to judge the value of subscribe, true to subscribe, false not to subscribe to the registration center;
    protected List<URL> loadRegistries(boolean provider) {
    
    
        // check && override if necessary
        List<URL> registryList = new ArrayList<URL>();
        if (CollectionUtils.isNotEmpty(registries)) {
    
    
            for (RegistryConfig config : registries) {
    
    
                String address = config.getAddress();
                // 如果注册中心没有配地址,则地址为0.0.0.0
                if (StringUtils.isEmpty(address)) {
    
    
                    address = ANYHOST_VALUE;
                }
                // 如果注册中心的地址不是"N/A"
                if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
    
    
                    Map<String, String> map = new HashMap<String, String>();
                    // 把application中的参数放入map中,注意,map中的key是没有prefix的
                    appendParameters(map, application);
                    // 把config中的参数放入map中,注意,map中的key是没有prefix的
                    // config是RegistryConfig,表示注册中心
                    appendParameters(map, config);
                    // 此处path值固定为RegistryService.class.getName(),因为现在是在加载注册中心
                    map.put(PATH_KEY, RegistryService.class.getName());
                    // 把dubbo的版本信息和pid放入map中
                    appendRuntimeParameters(map);

                    // 如果map中如果没有protocol,那么默认为dubbo
                    if (!map.containsKey(PROTOCOL_KEY)) {
    
    
                        map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
                    }

                    // 构造注册中心url,地址+参数
                    List<URL> urls = UrlUtils.parseURLs(address, map);

                    for (URL url : urls) {
    
    
                        url = URLBuilder.from(url)
                                .addParameter(REGISTRY_KEY, url.getProtocol())
                                .setProtocol(REGISTRY_PROTOCOL)
                                .build();

                        // 这里是服务提供者和服务消费者区别的逻辑
                        // 如果是服务提供者,获取register的值,如果为false,表示该服务不注册到注册中心
                        // 如果是服务消费者,获取subscribe的值,如果为false,表示该引入的服务不订阅注册中心中的数据
                        if ((provider && url.getParameter(REGISTER_KEY, true))
                                || (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
    
    
                            registryList.add(url);
                        }
                    }
                }
            }
        }
        return registryList;
    }

UrlUtils.parseURLs(address, map)

Purpose: To initially create a URL based on address and parameters; if the address format is: "192.168.2.110;192.168.2.111", it will be resolved into two URLs;

	//REGISTRY_SPLIT_PATTERN = ";"
    public static List<URL> parseURLs(String address, Map<String, String> defaults) {
    
    
        if (address == null || address.length() == 0) {
    
    
            return null;
        }
        String[] addresses = REGISTRY_SPLIT_PATTERN.split(address);
        if (addresses == null || addresses.length == 0) {
    
    
            return null; //here won't be empty
        }
        List<URL> registries = new ArrayList<URL>();
        for (String addr : addresses) {
    
    
            registries.add(parseURL(addr, defaults));
        }
        return registries;
    }

Traverse the ProtocolConfig protocol

  1. Obtain the URL collection of the registration center;
  2. Traverse the ProtocolConfig protocol;
  3. Get PathKey, format: group / contextPath / path :version; contextPath is the application name, path is the full path name;
  4. Create a ProviderModel instance, which wraps the path pathkey, service implementation class, and interface class information; ApplicationModel indicates that the service interface and interface implementation class in the application provide a service;
  5. Take the pathKey as the key and the providerModel instance as the value, and put it into the Map that caches PROVIDER_SERVICES
  6. Each protocol exports a separate service, which is registered to each registration center, and a service corresponds to one or more protocols;
	private List<ProtocolConfiguration> protocols;
	
    private void doExportUrls() {
    
    
        List<URL> registryURLs = loadRegistries(true);  
        for (ProtocolConfig protocolConfig : protocols) {
    
    
            //获取pathKey
            String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
			//创建ProviderModel实例
            ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
            //放入缓存
            ApplicationModel.initProviderModel(pathKey, providerModel);
			//一个服务,对应多种协议,每种一些导出一次,注册到注册中心;
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

Talking about Constructor Mode and Use

General concept: The builder mode is to create a complex object step by step, which allows users to build them only by specifying the type and content of complex objects, and users do not need to know the
specific internal construction details.

	url = URLBuilder.from(url).addParameter(REGISTRY_KEY, url.getProtocol()).setProtocol(REGISTRY_PROTOCOL).build();
	//获取参数, 创建构造器对象;
    public static URLBuilder from(URL url) {
    
    
        String protocol = url.getProtocol();
        String username = url.getUsername();
        String password = url.getPassword();
        String host = url.getHost();
        int port = url.getPort();
        String path = url.getPath();
        Map<String, String> parameters = new HashMap<>(url.getParameters());
        return new URLBuilder(
                protocol,
                username,
                password,
                host,
                port,
                path,
                parameters);
    }
    //添加参数,返回的还是自身实例
     public URLBuilder addParameter(String key, String value) {
    
    
        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
    
    
            return this;
        }
        // if value doesn't change, return immediately
        if (value.equals(parameters.get(key))) {
    
     // value != null
            return this;
        }

        parameters.put(key, value);
        return this;
    }
    //设置协议参数, 返回的还是自身实例
    public URLBuilder setProtocol(String protocol) {
    
    
        this.protocol = protocol;
        return this;
    }
    //使用构造器实例,创建URL对象。
    public URL build() {
    
    
        port = port < 0 ? 0 : port;
        // trim the leading "/"
        int firstNonSlash = 0;
		//...
        return new URL(protocol, username, password, host, port, path, parameters);
    }

  • Like in business, new Object(), and then set one by one, the amount of code is very large, and it is used in many places to create complex objects,
  • There is one more field in the database table, and one more field in the entity object, which is troublesome and has to be changed everywhere, which is very inconvenient. Therefore, this URLBuidler constructor is worth learning.
  • For example, if a field needs to be added to the URL, it only needs to be modified in the Builder constructor without modifying our business code. Reduce the amount of code changes,
    comply with the principle of opening and closing, and the principle of single responsibility;
    handle the configuration of the registry: convert it into URL objects one by one according to RegistryConfig, and finally return a URL collection;

Guess you like

Origin blog.csdn.net/yaoyaochengxian/article/details/123512598