In the previous section, when explaining the RegistryDirectory, the dubbo administrator can modify the parameters of dubbo service provider online through the dubbo admin management system, and finally store them in the configurators catalog of the registry, and then notify the RegistryDirectory to update the relevant attributes in the URL of the service provider, re create the Invoker and destroy the original Invoker according to the latest configuration. The details of dynamic change configuration (override protocol) in the official documents are as follows: Dubbo admin management background, the interface is as follows:
When Dubbo administrator in the above interface, select configuration and click save, the override:// url will be built and stored in the configuration center catalog. At this time, the registry directory of the service provider based on the registry will receive a callback (notify) method. Next, let's take a look at the registrydirectory ᦇ notify method.
RegistryDirectory#notify
public synchronized void notify(List<url> urls) { // @1 List<url> invokerUrls = new ArrayList<url>(); List<url> routerUrls = new ArrayList<url>(); List<url> configuratorUrls = new ArrayList<url>(); for (URL url : urls) { String protocol = url.getProtocol(); String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) { routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { configuratorUrls.add(url); } else if (Constants.PROVIDERS_CATEGORY.equals(category)) { invokerUrls.add(url); } else { logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()); } } // configurators if (configuratorUrls != null && configuratorUrls.size() >0 ){ // @2 this.configurators = toConfigurators(configuratorUrls); } // routers if (routerUrls != null && routerUrls.size() >0 ){ List<router> routers = toRouters(routerUrls); if(routers != null){ // null - do nothing setRouters(routers); } } List<configurator> localConfigurators = this.configurators; // local reference // Merge override parameters this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && localConfigurators.size() > 0) { for (Configurator configurator : localConfigurators) { this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } // providers refreshInvoker(invokerUrls); // @3 }
Due to the implementation of this method in the previous article Source code analysis Dubbo service discovery mechanism (registry directory) This article only lists the concerns related to this article Code @ 1: the parameter is all URL s under the current configurators directory, for example:
urls: [override://0.0.0.0/com.wuys.frame.api.service.IUserService?category=configurators&dynamic=false&enabled=true&timeout=10000, override://0.0.0.0/com.wuys.frame.api.service.IUserService?category=configurators&dynamic=false&enabled=true&weight=200].
Code @ 2: convert override url to list < configurator >, which is the focus of this section. Code @ 3: call the refreshInvoker method. Since the invokerUrls here is empty, will the new configuration parameters be applied to the original invoker right now? With this question in mind, let's take a look at how refreshInvoker is handled, and then focus on the implementation details of code @ 2. About the implementation of refreshInvoker, in the previous article Source code analysis Dubbo service registration and discovery mechanism RegistryDirectory) We have also analyzed it in detail here just to verify:
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){ invokerUrls.addAll(this.cachedInvokerUrls); } else { this.cachedInvokerUrls = new HashSet<url>(); this.cachedInvokerUrls.addAll(invokerUrls);//Cache the invokerUrls list for cross comparison } if (invokerUrls.size() ==0 ){ return; } // Omit some codes }
It can be seen from here that if the invokerurl is empty and the cached service provider is not empty, the cached service provider will be added to the invokerurl. At this time, the invokerurl is not empty, the new invoker will be generated with the new configuration again, and the original invoker will be destroyed.
Next, it focuses on the analysis and implementation details of Dubbo's override protocol.
1. dubbo on the override class diagram
- Configurator: protocol configuration interface, mainly abstracting two interface methods:
- URL getUrl(): get configuration URL.
- URL configure(URL url): configure the URL url according to the configureUrl.
- AbstractConfigurator: protocol configuration Abstract implementation class (template class).
- AbsentConfigurator: absent configurator. Its policy is not to overwrite the property of configureUrl if it exists.
- OverrideConfigurator: override configurator, whose policy is to directly override properties.
2. Source code analysis implementation principle of OverrideConfigurator
2.1 source code analysis abstractconfigurator × configure
AbstractConfigurator#configure
- Configurator: protocol configuration interface, mainly abstracting two interface methods:
- URL getUrl(): get configuration URL.
- URL configure(URL url): configure the URL url according to the configureUrl.
- AbstractConfigurator: protocol configuration Abstract implementation class (template class).
- AbsentConfigurator: absent configurator. Its policy is not to overwrite the property of configureUrl if it exists.
- OverrideConfigurator: override configurator, whose policy is to directly override properties.
3. Source code analysis implementation principle of OverrideConfigurator
3.1 source code analysis abstractconfigurator × configure
AbstractConfigurator#configure
public URL configure(URL url) { if (configuratorUrl == null || configuratorUrl.getHost() == null || url == null || url.getHost() == null) { // @1 return url; } if (configuratorUrl.getPort() != 0) { // @2 if (url.getPort() == configuratorUrl.getPort()) { return configureIfMatch(url.getHost(), url); // @3 } } else { if (url.getParameter(Constants.SIDE_KEY, Constants.PROVIDER).equals(Constants.CONSUMER)) { // @4 return configureIfMatch(NetUtils.getLocalHost(), url);// NetUtils.getLocalHost is the ip address consumer registered to registry. } else if (url.getParameter(Constants.SIDE_KEY, Constants.CONSUMER).equals(Constants.PROVIDER)) { // @5 return configureIfMatch(Constants.ANYHOST_VALUE, url); } } return url; }
Code @ 1: if configuratorUrl (configuration URL) is empty host is empty, or url is empty or host is empty, return url. Here, the coverage direction of the parameter configuratorUrl ----- > url. Code @ 2: if configuratorUrl is not empty, you need to determine the port of the url. The port must be the same before you can perform configuratorUrl configuration url. Code @ 3, perform specific configuration operation, the following is to be analyzed. Code @ 4, @ 5: if the port is empty, the configuration URL type is either for the consumer or the address is 0.0.0.0 (any).
If the url belongs to the service consumer, host is the registered IP address of the consumer. If the url belongs to the service provider, host is 0.0.0.0 to configure.
3.2 source code analysis abstractconfigurator ා configureifmatch
private URL configureIfMatch(String host, URL url) { if (Constants.ANYHOST_VALUE.equals(configuratorUrl.getHost()) || host.equals(configuratorUrl.getHost())) { String configApplication = configuratorUrl.getParameter(Constants.APPLICATION_KEY, configuratorUrl.getUsername()); String currentApplication = url.getParameter(Constants.APPLICATION_KEY, url.getUsername()); if (configApplication == null || Constants.ANY_VALUE.equals(configApplication) || configApplication.equals(currentApplication)) { Set<string> condtionKeys = new HashSet<string>(); condtionKeys.add(Constants.CATEGORY_KEY); condtionKeys.add(Constants.CHECK_KEY); condtionKeys.add(Constants.DYNAMIC_KEY); condtionKeys.add(Constants.ENABLED_KEY); for (Map.Entry<string, string> entry : configuratorUrl.getParameters().entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (key.startsWith("~") || Constants.APPLICATION_KEY.equals(key) || Constants.SIDE_KEY.equals(key)) { condtionKeys.add(key); if (value != null && !Constants.ANY_VALUE.equals(value) && !value.equals(url.getParameter(key.startsWith("~") ? key.substring(1) : key))) { return url; } } } return doConfigure(url, configuratorUrl.removeParameters(condtionKeys)); } } return url; }
The main function of this method is to exclude the properties that cannot be modified dynamically. The properties that are not supported mainly include category, check, dynamic, enabled and the properties starting with ~. If the properties starting with ~ have different values between the configuration URL and the original URL, the configuration URL is not used to rewrite the original URL. After removing the unsupported property from the configuratorurl, call the doConfigure method of its subclass to override the property. Dubbo supports the following override policy by default
- Override direct override.
- absent: if the configuration of this property exists, the value of the property configured previously takes precedence. If the property is not configured previously, a new configuration property will be added.
To summarize: after creating an override rule in Dubbo admin (administration background), it will first be stored in the registry (specified directory of zookeeper ${service}/configurators). At this time, based on the event mechanism of the registry, the relevant listener (service consumer) will be notified. When the service consumer receives the latest configuration, the invo will be rebuilt according to the latest configuration Ker object, and then destroy the original Invoker object.
The author introduces: Ding Wei, author of "RocketMQ technology insider", RocketMQ community sermons, public address: Middleware interest circle At present, maintainers have successively published source code analysis Java collection, Java Concurrent contract (JUC), Netty, Mycat, Dubbo, RocketMQ, Mybatis and other source code columns. You can click on the link: Middleware knowledge planet , discuss high concurrent and distributed service architecture and exchange source code together.
</string,></string></string></url></configurator></router></url></url></url></url></url></url></url>