dubbo source code analysis spring+dubbo case integration

Posted by ina on Thu, 17 Feb 2022 08:39:09 +0100

catalogue

 

1, spring+dubbo case integration

1. The project structure is as follows

2. dubbo service / consumption configuration

2, spring+dubbo portal analysis

1. dubbo dependency configuration

2. DubboNamespaceHandler analysis

2.1 dubbo common labels

 2.2 dubbo label analysis

 2.3 specific analysis

1, spring+dubbo case integration

Project github address: https://github.com/liushangzaibeijing/dubbodemo.git

1. The project structure is as follows

This project is a multi module maven project with parent-child dependency

  • Dubpoolddemo (root) parent project: it is mainly used to set the general maven dependency of the sub project (the version information of the general dependency is controlled uniformly, and Zilie does not need to declare)
  • Dubbo API (child) subproject: mainly general project dependencies (such as interfaces, entities, enumerations, constants)
  • dubbo core (child) subproject: service provider (service provider) of dubbo interface
  • dubbo OMS (child) subproject: service invocation of dubbo interface (service consumer)

2. dubbo service / consumption configuration

  • dubbo service provider configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- Specific implementation bean -->
    <bean id="demoService" class="com.xiu.dubbo.service.impl.DemoServiceImpl" />
    <!--  dubbo Related configuration  -->
    <!-- Provider application information for calculating dependencies -->
    <dubbo:application name="dubbo-core" />
    <!-- use zookeeper Registry service address,Multiple addresses are used in the middle,Number separation ?backup=192.168.137.131:2181,192.168.137.132:2181 -->
    <dubbo:registry address="zookeeper://182.92.189.235:2181" />
    <!-- use dubbo The protocol exposes services on port 20880 -->
    <dubbo:protocol name="dubbo" port="20881" />

    <!-- Declare the service interfaces that need to be exposed -->
    <!--<dubbo:service timeout="3000" interface="com.xiu.dubbo.service.DemoService" ref="demoService"-->
                   <!--cluster="failover" />-->
    <!-- It can also be processed in the form of annotations. The way of using annotations needs to be used by the service provider
         @Service(dubbo Own notes, not Spring) Service user usage@Reference Annotation modification  -->
    <dubbo:annotation package="com.xiu.dubbo.service" />
</beans>
  • dubbo service consumer configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- Provider application information for calculating dependencies -->
    <dubbo:application name="dubbo-oms" />
    <!-- use zookeeper Registry service address,Multiple addresses are used in the middle,Number separation ?backup=192.168.137.131:2181,192.168.137.132:2181 -->
    <dubbo:registry address="zookeeper://182.92.189.235:2181" />

    <!-- Declare the service interfaces that need to be exposed -->
    <dubbo:reference id="demoService" interface="com.xiu.dubbo.service.DemoService" />
    <!--Fail to switch automatically. In case of failure, retry other servers,Usually used for read operations, but retries can cause longer delays ,Can pass retries="2"To set the number of retries(Excluding the first time). 
    Default is failover,Can not write ,include(failfast/failback/forking) ,forks="2",Call multiple servers in parallel and return as long as one succeeds.
    It is usually used for reading operations with high real-time requirements, but it needs to waste more service resources. It allows you to set the maximum number of parallels. -->

    <!-- It can also be processed in the form of annotation. It is found that the unit test cannot reference the corresponding service in the form of annotation-->
    <!--<dubbo:annotation package="com.xiu.dubbo.service" />-->
    <bean id ="omsDemoService" class="com.xiu.dubbo.oms.service.impl.OmsDemoServiceImpl" />


</beans>
  • dubbo's maven dependency
<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.5.3</version>
      <exclusions>
         <exclusion>
            <groupId>org.springframework</groupId>
           <artifactId>spring</artifactId>
         </exclusion>
       </exclusions>
</dependency>

2, spring+dubbo portal analysis

1. dubbo dependency configuration

View the jar s corresponding to maven dependencies we introduced into dubbo as follows:

As shown in the figure above, it is the portal for the integration of dubbo and spring. There are three files and four parts in total. Let's explain the role of these files. Before analyzing these files, we need to have some knowledge about spring custom tags to understand the above files. For spring custom tags, please refer to my blog post: Analysis of custom tags in spring source code analysis.

  1.  dubbo.xsd file: the relevant constraint file configured by dubboxml enables users of custom tags to use tags according to specifications, rather than adding or modifying tag attributes at will, For example, when the < XS: due > attribute < BBO: due > and < BBO: due > attribute < BBO: due > are defined in the file, the order of the < XS: due > attribute appears.
  2. spring. Schema file: bind the xsd file of the corresponding namespace with the real xsd file to verify the custom label
  3. spring.handler file: bind the namespace with the corresponding naming processor, and let spring get the corresponding naming processor according to different namespaces for corresponding parsing
  4. dubbo namespacehandler: the naming processor is used to parse the related xml configuration of dubbo.

From the above analysis, we can know that the reason why dubbo can be used after being configured through xml is that dubbo namespacehandler parses relevant tags and generates corresponding bean instances, which are managed by the spring container. Let's analyze it below

The parsing process of DubboNamespaceHandler.

2. DubboNamespaceHandler analysis

2.1 dubbo common labels

Label informationLabel description
<dubbo:application />Used to configure application information and dubbo calculate its dependencies
<dubbo: module />Used to configure application module information (applicable to one application with multiple modules)
<dubbo:protocol />Used to configure the access protocol of the service provider
<dubbo:registry />It is used to configure four kinds of registration centers: Multicast, Zookeeper, Redis and Dubbo
<dubbo:provider />It is used to configure the general configuration value of the service provider. The provider is the original service providing method: there are too many configuration parameters, which are cumbersome. The configuration on it can be directly inherited by all < Dubbo: Service / > by default, that is, set the default values of < Dubbo: Service > and < Dubbo: Protocol > tags,
<dubbo:service />Used to configure service providers to expose their own services
<dubbo:consumer />It is used to configure the default value of the service consumer, that is, the default value of the < Dubbo: reference > tag, which has the same relationship with < Dubbo: provider / > and < Dubbo: Service / >
<dubbo:reference />Used to configure the service consumer reference service
<dubbo:annotation />Support annotation scanning to register service and reference service
<dubbo: monitor />Configuration of dubbo monitoring module

 

 

 

 

2.2 dubbo label analysis

The init method of DubboNamespaceHandler will submit all the tags of dubbo to the dubbo beandefinitionparser for parsing. It is probably known from the name that its function is to parse and convert the dubbo tag into a BeanDefinition object

Readers who have understood the bean instantiation process of spring source code know that BeanDefinition is the abstraction of all spring managed bean instances, including our actual beanClass, which will be applied at the right time The refresh () method is instantiated as a spring managed bean instance. For BeanDefinition analysis, refer to: BeanDefinition related to spring source code analysis.

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

  2.3 specific analysis

The DubboBeanDefinitionParser parse() method is a parsing process for the xml configuration of dubbo related tags.

//< Dubbo: XXXX > specific parsing logic of relevant Tags  
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        //Initialize a beandefinition object (beandefinition of the class corresponding to ApplicationConfig and ServiceConfig in the above figure)
        //Used for subsequent generation of corresponding bean instances by spring
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        //Get (generate) the id of the bean
        // The logic is as follows: directly obtain the id attribute, not the name, and not the interface attribute (the special treatment of ProtocolConfig is dubbo)
        //If the className is not obtained, then judge whether it already exists. After the id exists, splice the number, and finally put the obtained id into the MutablePropertyValues (property collection) of BeanDefinition
        String id = element.getAttribute("id");
        if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("name");
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                if (ProtocolConfig.class.equals(beanClass)) {
                    generatedBeanName = "dubbo";
                } else {
                    generatedBeanName = element.getAttribute("interface");
                }
            }
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                generatedBeanName = beanClass.getName();
            }
            id = generatedBeanName;
            int counter = 2;
            while(parserContext.getRegistry().containsBeanDefinition(id)) {
                id = generatedBeanName + (counter ++);
            }
        }
        if (id != null && id.length() > 0) {
            if (parserContext.getRegistry().containsBeanDefinition(id))  {
                throw new IllegalStateException("Duplicate spring bean id " + id);
            }
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
            beanDefinition.getPropertyValues().addPropertyValue("id", id);
        }
        //The main reason for parsing < Dubbo: Protocol > is that multiple can be configured. Here, multiple can be merged
        if (ProtocolConfig.class.equals(beanClass)) {
            for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
                BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
                PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
                if (property != null) {
                    Object value = property.getValue();
                    if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                        definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                    }
                }
            }
        }
        //The processing of < dubbo:service > is mainly to wrap its class attribute into beandefinition and store it as ref attribute in the bean corresponding to dubbo:service
        else if (ServiceBean.class.equals(beanClass)) {
            String className = element.getAttribute("class");
            if(className != null && className.length() > 0) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                classDefinition.setBeanClass(ReflectUtils.forName(className));
                classDefinition.setLazyInit(false);
                parseProperties(element.getChildNodes(), classDefinition);
                beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        }
        //The processing of < dubbo:provider > is mainly to resolve its sub attributes < Dubbo: Service >, in which the attributes in dubbo:provider are applicable to < Dubbo: Service >
        else if (ProviderConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
        }
        ///The processing of < Dubbo: consumer > is the same as that of < Dubbo: provider >. The former is configured by consumers and the latter is configured by servers
        else if (ConsumerConfig.class.equals(beanClass)) {
            parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
        }
        //Get all the methods of related dubbo XXXConfig, call its setter method to get the properties of the corresponding xml configuration and perform relevant parsing
        Set<String> props = new HashSet<String>();
        ManagedMap parameters = null;
        for (Method setter : beanClass.getMethods()) {
           //Omit the complex attribute parsing (get its setXXX, where xxx should be the attribute of the corresponding xml, and parse and store its configured attribute)
        }
        //Get other attribute information related to dubbo XXXConfig and store it in the parameters of the attribute object
        NamedNodeMap attributes = element.getAttributes();
        int len = attributes.getLength();
        for (int i = 0; i < len; i++) {
            Node node = attributes.item(i);
            String name = node.getLocalName();
            if (! props.contains(name)) {
                if (parameters == null) {
                    parameters = new ManagedMap();
                }
                String value = node.getNodeValue();
                parameters.put(name, new TypedStringValue(value, String.class));
            }
        }
        if (parameters != null) {
            beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
        }
        //Finally, the corresponding beanDefinition object is returned
        return beanDefinition;
    }

The above code is really long, but after specific analysis, we find that its logic is very simple and can be roughly divided into two parts

  • Initialize a BeanDefinition(RootBeanDefinition object) and set the corresponding class attribute of < Dubbo: XXX > (the skeleton has been processed). The beandefinition will be instantiated during the startup of the spring container
  • Next, analyze different < Dubbo: XXX > detailed data, such as id attribute, attributes attribute and class attribute of service, as well as the attribute of obtaining method name. Finally, these resolved attributes will be stored in the MutablePropertyValues (multi-attribute object) of beanDefinition (equivalent to the flesh and blood filling the beanDefinition skeleton).