Detailed explanation of DUBBO configuration rules

I have been studying DUBBO for more than half a year. I have analyzed most of its source code, and have a relatively in-depth understanding of its internal mechanism and the implementation of each module. DUBBO contains a lot of content. If you want to understand DUBBO, the first step is to start it, so that you can use it well, so how to use it better? You need to know the various configuration items of DUBBO and how it can be configured. Personal understanding of the configuration is like the tame of animals. How to tame a beast well, you need to know its various habits, so as to adjust, and achieve the desired result. This article does not know which configuration items can be configured for DUBBO, but through this article, you should be able to know what configurations DUBBO can perform. This article will analyze the DUBBO loading configuration source code analysis, so that everyone can have a more in-depth understanding of the DUBBO configuration. So as to "tame" DUBBO to make it your own DUBBO.

DUBBO has done a perfect job in the configuration, providing many parameters and various channels. Let's enter the topic below and see how DUBBO loads the configuration. Before talking about these, let me introduce which classes are defined at the source level of DUBBO to store the configuration items of each module, so as to understand which modules DUBBO can configure.

what can be configured

Since most projects will use Spring, and DUBBO also provides configuration through Spring, start here first. When DUBBO loads Spring's integration, under the dubbo-config-spring module under dubbo-config, there is a class DubboNamespaceHandler, which implements the interface NamespaceHandlerSupport provided by Spring. So how does Spring discover the entire implementation class? There are two files in the META-INF folder of this module: spring.handlers and spring.schemas. These two files specify the location of the XSD file of dubbo's namespace and the dubbo's namespace is processed and parsed by DubboNamespaceHandler. Having said so much nonsense, I just want to explain how Spring parses the <dubbo:.../> configuration.

After knowing how DUBBO and Spring are integrated when configuring one piece, then you should not be surprised how Spring is so smart that it can parse the namespace of dubbo. Next, take a look at what's in the DubboNamespaceHandler class.

<!--lang:java-->
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }

}
It can be seen that a method registerBeanDefinitionParser is called in the init method, but the parameters are slightly different. The first parameter of the registerBeanDefinitionParser method is the name of the node under dubbo's namespace, and the second parameter is who parses the node. For example: registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); is to parse <dubbo:application../> configuration information, and so on through Spring to configure <dubbo:module../>,< dubbo:registry../> and so on, I will not list them one by one. As for the role of each tag configuration, since it is not the content of this article, I will not introduce too much here.

Through the above, it should be clear what DUBBO can configure? This problem should no longer bother you. Let's see how DUBBO loads these configuration items.

How to read our configuration Read

through Spring's Bean configuration

In the project, Spring's XML will be configured (although DUBBO also supports the form of annotations, but I don't like it very much, because this will integrate DUBBO into your project source code, and My suggestion is not to say that DUBBO is too tightly tied to your project. My suggestion is to isolate DUBBO configuration and project, so as to facilitate management. After joining, the project will not use DUBBO, but use other distributed RPC frameworks. The adjustment will be relatively small), for example, a remote service is often referenced through the following configuration items:

<!--lang:xml-->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" timeout="2000" check="false"/>
Through the above content, you should know that dubbo:reference will be defined by new DubboBeanDefinitionParser( ReferenceBean.class, false) to parse (although it seems that all configurations are parsed with DubboBeanDefinitionParser, but there is a big difference). Then look at what is done in the class DubboBeanDefinitionParser.

<!--lang:java-->
public class DubboBeanDefinitionParser implements BeanDefinitionParser {

private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);

private final Class<?> beanClass;

private final boolean required;

public DubboBeanDefinitionParser(Class<? > beanClass, boolean required) {
    this.beanClass = beanClass;
    this.required = required;


public BeanDefinition parse(Element element, ParserContext parserContext) {
    return parse(element, parserContext, beanClass, required);
}
@SuppressWarnings("unchecked")
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {....}
}
First look at the class definition, DubboBeanDefinitionParser implements Spring's BeanDefinitionParser interface, which is specially used to parse Bean definitions (you should know when you look at the class name), and implements the public BeanDefinition parse(Element element, ParserContext parserContext) method ( Don't look at the whole method returning a BeanDefinition object, in fact, Spring does not use the entire returned object, you can look at the Spring source code, so to inject the Bean definition into the Spring container, you need to manually inject it into Spring, Because Spring did not give us to do this please.), and then called the static method private static BeanDefinition parse(...), then the class is mainly on the static method parse. Since the content of this method is too long, it is inconvenient to paste the entire content, so I only analyze the main part. There is a Class<?> beanClass parameter on the DubboBeanDefinitionParser constructor parameter, which specifies that the current label configuration content is converted into the BeanDefinition of the corresponding class and injected into the Spring container.

<!--lang:java-->
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClass(beanClass);
    beanDefinition.setLazyInit(false);
    String id = element.getAttribute("id");
    if ((id == null || id.length() == 0) && required) {
        //构造一个Bean的ID
    }
    ....
    parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
    ....
     for (Method setter : beanClass.getMethods()) {
        String name = setter.getName();
        if (name.length() > 3 && name.startsWith("set")
                && Modifier.isPublic(setter.getModifiers())
                && setter.getParameterTypes().length == 1) {
            Class<?> type = setter.getParameterTypes()[0];
            String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
            props.add(property);
            Method getter = null;
            try {
                getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
            } catch (NoSuchMethodException e) {
                try {
                    getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                } catch (NoSuchMethodException e2) {
                }
            }
            if (getter == null
                    || ! Modifier.isPublic(getter.getModifiers())
                    || ! type.equals(getter.getReturnType())) {
                continue;
            }
            if ("parameters".equals(property)) {
                parameters = parseParameters(element.getChildNodes(), beanDefinition);
            } else if ("methods".equals(property)) {
                parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
            } else if ("arguments".equals(property)) {
                parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
            }
        .....
        beanDefinition.getPropertyValues().addPropertyValue(property, reference);
        ......
The above code obviously sees that the get/set method of the class is obtained by reflection, so as to determine whether the parameter can be injected through Spring, and finally added to the beanDefinition and injected into the Spring container. The above is the mechanism for loading configuration from Spring.

Run the command -D and get the configuration through java. The

above content shows which modules DUBBO can configure and which classes are used to store these configuration information, such as: ReferenceBean, RegistryConfig, ServiceBean, etc. If you go in and see the definitions of these classes You will find that they all inherit the AbstractConfig abstract class, which defines the protected static void appendProperties(AbstractConfig config) method. The parameter is a subclass that implements itself. Next, let's see what it does:

<!--lang :java-->
protected static void appendProperties(AbstractConfig config) {
    if (config == null) {
        return;
    }
    String prefix = "dubbo." + getTagName(config.getClass()) + ".";
    Method[] methods = config.getClass().getMethods();
    for (Method method : methods) {
        try {
            String name = method.getName();
            if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {
                String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");

                String value = null;
                if (config.getId() != null && config.getId().length() > 0) {
                    String pn = prefix + config.getId() + "." + property;
                    value = System.getProperty(pn);
                    if(! StringUtils.isBlank(value)) {
                        logger.info("Use System Property " + pn + " to config dubbo");
                    }
                }
                if (value == null || value.length() == 0) {
                    String pn = prefix + property;
                    value = System.getProperty(pn);
                    if(! StringUtils.isBlank(value)) {
                        logger.info("Use System Property " + pn + " to config dubbo");
                    }
                }
                if (value == null || value.length() == 0) {
                    Method getter;
                    try {
                        getter = config.getClass().getMethod("get" + name.substring(3), new Class<?>[0]);
                    } catch (NoSuchMethodException e) {
                        try {
                            getter = config.getClass().getMethod("is" + name.substring(3), new Class<?>[0]);
                        } catch (NoSuchMethodException e2) {
                            getter = null;
                        }
                    }
                    if (getter != null) {
                        if (getter.invoke(config, new Object[0]) == null) {
                            if (config.getId() != null && config.getId().length() > 0) {
                                value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
                            }
                            if (value == null || value.length() == 0) {
                                value = ConfigUtils.getProperty(prefix + property);
                            }
                            if (value == null || value.length() == 0) {
                                String legacyKey = legacyProperties.get(prefix + property);
                                if (legacyKey != null && legacyKey.length() > 0) {
                                    value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
                                }
                            }

                        }
                    }
                }
                if (value != null && value.length() > 0) {
                    method.invoke(config, new Object[] {convertPrimitive(method.getParameterTypes()[0], value)});
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
}
The above method starts by producing the prefix of the current configuration String prefix = "dubbo." + getTagName(config.getClass()) + " .", here you can see that all the configuration items of dubbo start with dubbo., and then generate the configuration name of the current configuration class through the getTagName method, and go in and see how getTagName generates its own configuration name for each configuration class of.

<!--lang:java-->
private static final String[] SUFFIXS = new String[] {"Config", "Bean"};
private static String getTagName(Class<?> cls) {
    String tag = cls.getSimpleName();
    for (String suffix : SUFFIXS) {
        if (tag.endsWith(suffix)) {
            tag = tag.substring(0, tag.length() - suffix.length());
            break;
        }
    }
    tag = tag.toLowerCase();
    return tag;
}
Analysis of the above code is very clear to know how to generate the configuration name of the respective configuration class, for example: ReferenceBean will return the reference through this method. So the configuration about referencing remote beans starts with dubbo.reference. After the prefix of the current configuration class is generated, all the set methods of the current class are reflected according to the management, and the next step is to take it from System.getProperty. If there is none, it will be taken from ConfigUtils.getProperty. It should be noted that: taking the value from System.getProperty does not determine whether the current property has a value (injected by spring), but taking it from ConfigUtils.getProperty will determine whether there is a value, if not, it will take it from it, here It can be explained that the priority of the DUBBO configuration item java -D takes precedence over the Spring configuration, and the Spring configuration takes precedence over the configuration of the properties file, which also conforms to the rules of general projects. I don't know if you have noticed, whether it is System.getProperty or ConfigUtils.getProperty, it will be taken twice, one is String pn = prefix + config.getId() + "." + property, and the other is String pn = prefix + property, The difference between the two is that there is one more config.getId(), you may be wondering what the content of config.getId() is? When introducing Spring loading configuration, you should know that the config object is actually in the Spring container, so the getId here is actually the ID we configured when configuring the Spring Bean. For example:

<!--lang:java-->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" timeout="
The configuration will generate a ReferenceBean object, then config.getId() at this time is demoService. So the purpose of String pn = prefix + config.getId() + "." + property configuration is whether there is a configuration item for a current bean. For example: to configure the timeout time of the dubbo consumer, generally through dubbo.reference.timeout, this is actually the timeout time of all beans on the specified consumer. Sometimes we need to specify the timeout time of a bean, which can be specified by dubbo.reference.{beanId}.timeout. For example, the timeout time of the bean can be configured by dubbo.reference.demoService.timeout above.

So far, I have analyzed and introduced all the configuration methods and principles of DUBBO. It seems that I haven't said how to find out what configurations of DUBBO. Here to teach you how to find out what configuration DUBBO has. As mentioned above, DUBBO sets configuration items into the entities of each configuration class by judging whether there is a get/set method. At this point, everyone should know how to find out, right? That is, if you want to know the configuration of a module of dubbo, go directly to the corresponding configuration class to see what fields it has, know its field names, and determine the scope you want to configure, whether it is global or a bean, and what you want to pass What way to configure, according to the rules provided by dubbo to determine how to configure the corresponding fields. Then you may say that a word in the field knows how to configure it, so what if the field is a combination of multiple words? Since the coding style of java is generally in camel case format: the first letter of the first word is lowercase and the first letter of the word is uppercase, so how to obtain this configuration item for dubbo in this case? See below:

<!--lang:java-->
  //name is the name of the get/set method
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
It is not difficult to find that for camel case, dubbo uses - character to separate each word. In fact, you can basically configure DUBBO well here, but there is a way above. If you need to adjust the parameters, you need to restart the application, which cannot achieve dynamic adjustment, so dubbo can be dynamically adjusted in order to meet the requirements of the project without restarting. Configuration parameters.

Add dynamic configuration via DUBBO management app

The main principle of this method is to publish dynamic parameters to the registration center (zookeeper) through the manager, and then each node can obtain the latest configuration changes and then make dynamic adjustments. To know this, you need to take a look at the implementation of the class RegistryDirectory, which implements NotifyListener. Then it can listen to any changes in the registry. Before learning about RegistryDirectory, let's take a look at what information DUBBO stores in the registry for each service? If you use zookeeper, then you will see several directories consumers, providers, configurators, routers under each service node directory. The dynamic configuration is placed in the configurators node directory. The service consumer will listen for changes in the configurators directory, and if it changes, it will call the void notify(List<URL> urls) method of the RegistryDirectory. When listening to the void notify(List<URL> urls) method triggered by the change of the configurators directory, the urls are similar to override://..., which means that some configurations of calling the service will be overridden (all the calling configurations in dubbo are It is displayed in the form of URL), replace the parameter information on these URLs with the URL of the calling server, and reconstruct the Invoke object of the service, so as to achieve the purpose of updating parameters.

You can configure parameters for interface methods.

The entire scenario exists in actual projects. For example, an interface has multiple methods. Sometimes parameter configuration is not enough at the interface level. It needs to be accurate to the method level, such as the timeout of the interface call. time setting. Dubbo provides two ways to configure at the method level.

The way to configure Spring Bean is configured

as follows :

<!--lang:xml-->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" timeout="2000" check="false" >
        <dubbo:method name="sayHello" timeout="1000"/>
< /dubbo:reference>
is implemented by nesting one more dubbo:method tag.

Dynamic

configuration The manager of DUBBO provides the function of dynamic configuration. By adding dynamic configuration and specifying the consumer end of the notification, you can specify a certain method of a service to adjust the parameters. The rule configured there is the method name plus the name of the configuration item, for example: the key is configured as: sayHello.timeout, value="10000". Then the manager will add an override://...?sayHello.timeout=10000 node to the configurators of the corresponding service in the registry, and the specified consumer will listen to this change and execute the above process.

At this point, the DUBBO configuration has been introduced, and there may be some places that have not been clearly explained, and there are doubts. If you have any questions, please ask questions or take a look at the relevant DUBBO source code yourself, it will definitely understand more clearly.

Excerpted from: https://my.oschina.net/bieber/blog/378638

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326570018&siteId=291194637