Distributed RPC framework Apache Dubbo

Posted by erth on Thu, 06 Jan 2022 16:17:58 +0100

1. Evolution process of software architecture

The development of software architecture has experienced the evolution process from single architecture, vertical architecture, SOA architecture to micro service architecture. Let's learn about these architectures respectively.

1.1 single structure


Architecture Description:

  • All functions are concentrated in one project.

Architecture advantages:

  • Simple structure, low cost of early development and short development cycle, suitable for small projects.

Architecture disadvantages:

  • All functions are integrated in one project, which is difficult to develop, expand and maintain for large projects.
  • The technology stack is limited and can only be developed in one language.
  • The system performance can only be expanded by expanding cluster nodes, which is costly.

1.2 Vertical Architecture


Architecture Description:

  • Cut according to business to form small single projects.

Architecture advantages:

  • The technology stack is extensible (different systems can be written in different programming languages).

Architecture disadvantages:

  • Functions are concentrated in one project, which is not conducive to development, expansion and maintenance.
  • The system can only be expanded through clusters.
  • Functional redundancy, data redundancy and strong coupling between projects.

1.3 SOA Architecture

The full name of SOA is Service-Oriented Architecture, that is, Service-Oriented Architecture. It can deploy, combine and use loosely coupled coarse-grained application components (services) through the network according to the needs. A service usually exists in an independent form in the operating system process.
From the perspective of function, abstract the business logic into reusable services, and realize the rapid regeneration of business through the arrangement of services. The purpose is to transform the original inherent business functions into general business services and realize the rapid reuse of business logic.

Architecture Description:

  • The repeated functions or modules are extracted into components to provide services externally, and the form of ESB (Enterprise Service Bus) is used as the communication bridge between projects and services.

Architecture advantages:

  • Duplicate functions or modules are extracted as services to improve development efficiency.
  • High reusability.
  • High maintainability.

Architecture disadvantages:

  • The business of each system is different, so it is difficult to confirm that the functions or modules are repeated.
  • The granularity of extraction service is large.
  • High coupling between systems and services.

1.4 microservice architecture


Architecture Description:

  • The system service layer is completely independent and extracted into micro services one by one.
  • The granularity of extraction is finer and follows the single principle. It adopts lightweight framework protocol transmission.

Architecture advantages:

  • The granularity of service splitting is finer, which is conducive to improving development efficiency.
  • Corresponding optimization schemes can be formulated for different services.
  • It is applicable to the Internet era, and the product iteration cycle is shorter.

Architecture disadvantages:

  • Too fine granularity leads to too many services and high maintenance costs.
  • The technical cost of distributed system development is high and poses great challenges to the team.

2. Apache Dubbo overview

2.1 introduction to Dubbo

Apache Dubbo is a high-performance Java RPC framework. Its predecessor is Alibaba's open source and lightweight open source Java RPC framework, which can be seamlessly integrated with the Spring framework. In 2018, Alibaba donated this framework to the apache foundation.
What is RPC?
The full name of RPC is remote procedure call, that is, remote procedure call. For example, there are two servers A and B. an application is deployed on server A and an application is deployed on server B. The Application on server A wants to call the methods provided by the application on server B. because the two applications are not in the same memory space and cannot be called directly, it is necessary to express the calling semantics and convey the calling data through the network.
It should be noted that RPC is not a specific technology, but refers to the whole network remote call process.
RPC is a generalized concept. Strictly speaking, all remote procedure call methods belong to the category of RPC. Various development languages have their own RPC framework. There are many RPC frameworks in Java, such as RMI, Hessian, Dubbo, etc.
Dubbo official website address: http://dubbo.apache.org
Dubbo offers three core competencies:

  1. Interface Oriented Remote Method Invocation
  2. Intelligent fault tolerance and load balancing
  3. Automatic service registration and discovery

2.2 Dubbo architecture

Dubbo architecture diagram (officially provided by Dubbo) is as follows:

Node role description:

nodeRole name
ProviderService provider of exposed services
ConsumerThe service consumer that invokes the remote service
Registry (Registry)Registry for service registration and discovery
MonitorThe monitoring center that counts the number and time of service calls
ContainerService run container

The dotted line is asynchronous access, and the solid line is synchronous access
Blue dotted line: functions completed at startup
Red dotted line / solid line: functions performed during program operation

Description of calling relationship:

  1. The service container is responsible for starting, loading and running the service provider.
  2. When a service provider starts, it registers its services with the registry.
  3. When the service consumer starts, it subscribes to the service it needs from the registry.
  4. The registry returns the service provider address list to the consumer. If there is any change, the registry will push the change data to the consumer based on the long connection.
  5. From the provider address list, the service consumer selects one provider to call based on the soft load balancing algorithm. If the call fails, it selects another provider to call.
  6. Service consumers and providers accumulate call times and call times in memory, and regularly send statistical data to the monitoring center every minute.

3. Service registry Zookeeper

As can be seen from the previous Dubbo architecture diagram, Registry (service registry) plays a vital role. Dubbo officially recommends using Zookeeper as the service registry.

3.1 introduction to zookeeper

Zookeeper is a sub project of Apache Hadoop. It is a tree type directory service that supports change push. It is suitable to be used as the registry of Dubbo service. It has high industrial intensity, can be used in production environment, and is recommended.
In order to understand Zookeeper's tree directory service, let's take a look at our computer's file system (also a tree directory structure):

My computer can be divided into multiple drive letters (such as C, D, E, etc.), and multiple directories can be created under each drive letter. Files or subdirectories can be created under each directory, which finally forms a tree structure. Through this tree structure directory, we can store files by categories, which is convenient for us to find later. And each file on the disk has a unique access path, for example: C: \ windows \ KKB \ hello txt.
Zookeeper tree directory service:

Process Description:

  • When a service provider starts: to / Dubbo / com foo. Write your own URL address in the barservice / providers directory
  • When a service consumer starts: Subscribe / Dubbo / com foo. The URL address of the provider in the barservice / providers directory. And to / Dubbo / com foo. Write your own URL address in the barservice / consumers directory
  • When the monitoring center starts: subscribe to / Dubbo / com foo. URL addresses of all providers and consumers under the barservice directory

3.2 installation of Zookeeper

Download address: http://archive.apache.org/dist/zookeeper/
The Zookeeper version used in this example is 3.4.6. After downloading, you can get the name Zookeeper-3.4.6 tar. GZ compressed file.

Installation steps:

  1. Step 1: install jdk (omitted).
  2. Step 2: upload zookeeper's compressed package (zookeeper-3.4.6.tar.gz) to linux system.
  3. Step 3: decompress the compressed package tar -zxvf zookeeper-3.4.6 tar. gz -C /myapps.
  4. Step 4: enter the zookeeper-3.4.6 directory and create the data directory mkdir data.
  5. Step 5: enter the conf directory and put zoo_sample.cfg is renamed zoo cfg. cd conf mv zoo_sample.cfg zoo.cfg.
  6. Step 6: open zoo Cfg file, modify the data attribute: dataDir = / usr / zookeeper3 4.6/data

3.3 start and stop Zookeeper

Enter the bin directory of Zookeeper
Start service command:/ zkServer.sh start
Stop service command:/ zkServer.sh stop
View service status:/ zkServer.sh status
Client connection:/ zkCli.sh
The default port number is 2181

4. Dubbo quick start

As an RPC framework, Dubbo's core function is to realize cross network remote call. This section is to create two applications, one as a service provider and the other as a service consumer. Dubbo enables the service consumer to remotely call the method of the service provider.

4.1 service provider development

4.1.1 pom.xml file:
Create a maven project (packaged as war) dubcodemo_ Provider, in POM Import the following coordinates into the XML file

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<maven.compiler.source>1.8</maven.compiler.source>
	<maven.compiler.target>1.8</maven.compiler.target>
	<spring.version>5.0.5.RELEASE</spring.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-beans</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jms</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context-support</artifactId>
		<version>${spring.version}</version>
	</dependency>
	<!-- dubbo relevant -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>dubbo</artifactId>
		<version>2.6.0</version>
	</dependency>
	<dependency>
		<groupId>org.apache.zookeeper</groupId>
		<artifactId>zookeeper</artifactId>
		<version>3.4.7</version>
	</dependency>
	<dependency>
		<groupId>com.github.sgroschupf</groupId>
		<artifactId>zkclient</artifactId>
		<version>0.1</version>
	</dependency>
	<dependency>
		<groupId>javassist</groupId>
		<artifactId>javassist</artifactId>
		<version>3.12.1.GA</version>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>fastjson</artifactId>
		<version>1.2.47</version>
	</dependency>
</dependencies>
<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>2.3.2</version>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
			</configuration>
		</plugin>
		<plugin>
			<groupId>org.apache.tomcat.maven</groupId>
			<artifactId>tomcat7-maven-plugin</artifactId>
			<configuration>
				<!-- Specify port -->
				<port>8081</port>
				<!-- Request path -->
				<path>/</path>
			</configuration>
		</plugin>
	</plugins>
</build>

4.1.2 configuring web XML file

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

4.1.3 create service interface

package com.lxs.service;
public interface HelloService {
    public String sayHello(String name);
}

4.1.4 create service implementation class

package com.lxs.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.lxs.service.HelloService;
@Service
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "hello " + name;
    }
}

Note: the Service annotation used on the Service implementation class is provided by Dubbo and is used to publish services.
4.1.5 applicationContext-service.xml
Create ApplicationContext service under src/main/resources xml

    <!-- The current application name is used to calculate the dependency between applications in the registry. Note: the application names of consumers and providers are different -->
    <dubbo:application name="dubbodemo_provider" />
    <!-- Connect service registry zookeeper. ip by zookeeper Server ip address-->
    <dubbo:registry address="zookeeper://192.168.134.129:2181"/>
    <!--The protocol and port registered by the service. The default port is 20880-->
    <dubbo:protocol name="dubbo" port="20881"></dubbo:protocol>
    <!-- Scan the specified package and join@Service Annotated classes are published as services -->
    <dubbo:annotation package="com.lxs.service.impl" />
</beans>

4.1.6 start service
tomcat7:run

4.2 service consumer development

4.2.1 create project
Create a maven project (packaged as war) dubcodemo_ consumer,pom. The XML configuration is the same as the above service provider. You only need to change the port number of Tomcat plug-in to 8082
4.2.2 configuring web XML file

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Specify the configuration file to load through parameters contextConfigLocation load -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext-web.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

4.2.3 write service interface
Copy the HelloService interface in the service provider project to the current project
4.2.4 writing controller

package com.lxs.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.lxs.service.HelloService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/demo")
public class HelloController {
    @Reference
    private HelloService helloService;
    @RequestMapping("/hello")
    @ResponseBody
    public String getName(String name){
        //Remote call
        String result = helloService.sayHello(name);
        System.out.println(result);
        return result;
    }
}

Note: the @ Reference annotation provided by Dubbo is used to inject HelloService into the Controller
4.2.5 applicationContext-web.xml
Create ApplicationContext web under src/main/resources xml

<?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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- The current application name is used to calculate the dependency between applications in the registry. Note: the application names of consumers and providers are different -->
    <dubbo:application name="dubbodemo-consumer" />
    <!-- Connect service registry zookeeper ip by zookeeper Server ip address-->
    <dubbo:registry address="zookeeper://192.168.134.129:2181"/>
    <!-- Expose the interface by scanning -->
    <dubbo:annotation package="com.lxs.controller" />
</beans>

4.2.6 operation test
Start: tomcat7:run
Enter in the browser http://localhost:8082/demo/hello.do?name=Jack , view the browser output.

4.3 thinking

Think 1: in the Dubbo introduction case above, we copied the HelloService interface from the dubcodemo_provider to the dubcodemo_consumer. Is this appropriate? Is there a better way?
A: This is obviously bad. The same interface has been copied twice, which is not conducive to later maintenance. A better way is to create a maven project separately and create this interface in the Maven project. Projects that need to rely on this interface only need to be in their own POM Just import Maven coordinates into the XML file.
Thinking 2: in the Dubbo demo_consumer project, only the HelloService interface is referenced, and no implementation class is provided. How does Dubbo make remote calls?
A: the underlying layer of Dubbo creates a proxy object for the HelloService interface based on proxy technology, and remote calls are completed through this proxy object. You can view the internal structure of this proxy object through the debug function of the development tool. In addition, Dubbo implements the underlying network transmission based on the Netty framework.
Thinking 3: in the Dubbo introduction case above, we use Zookeeper as the service registry. Service providers need to register their service information with Zookeeper, and service consumers need to subscribe to the services they need from Zookeeper. At this time, Zookeeper service becomes very important. How to prevent Zookeeper single point of failure?
A: zookeeper actually supports cluster mode. Zookeeper cluster can be configured to achieve high availability of zookeeper services and prevent single point of failure.

5. Dubbo management console

During development, we need to know which services are registered in the Zookeeper registry and which consumers consume these services. We can do this by deploying a management center. In fact, the management center is a web application, which can be deployed to tomcat.

5.1 installation

  1. Dubbo-admin-2.6.0 Copy the war file to the webapps directory of tomcat
  2. Start tomcat and the war file will be decompressed automatically
  3. Modify Dubbo. Under WEB-INF Properties file, note Dubbo registry. The value corresponding to address needs to correspond to the ip address and port number of the Zookeeper currently used:
    dubbo.registry.address=zookeeper://192.168.134.129:2181
    dubbo.admin.root.password=root
    dubbo.admin.guest.password=guest
  4. Restart tomcat

5.2 use

  1. visit http://localhost:8080/dubbo-admin-2.6.0 /, enter user name (root) and password (root)
  2. Start the service provider project and the service consumer project to view the corresponding information



6. Configuration description of Dubbo

6.1 package scanning

<dubbo:annotation package="com.lxs.service" />

Both service providers and service consumers need to be configured to represent package scanning, which is used to scan classes under specified packages (including sub packages).
If package scanning is not used, services can also be published through the following configuration:

<bean id="helloService" class="com.lxs.service.impl.HelloServiceImpl" />
<dubbo:service interface="com.lxs.api.HelloService" ref="helloService" />

As a service consumer, services can be referenced through the following configuration:

<!-- Generate a remote service proxy, which can communicate with local bean Same use helloService -->
<dubbo:reference id="helloService" interface="com.lxs.api.HelloService" />

The above method publishes and references services. A configuration item (dubbo:service, dubbo:reference) can only publish or reference one service. If there are multiple services, this method is cumbersome. Package scanning is recommended.

6.2 agreement

<dubbo:protocol name="dubbo" port="20880"/>	

Generally, it is configured on the service provider side, and the protocol name and port number can be specified.
The protocols supported by Dubbo include Dubbo, rmi, hessian, http, webservice, rest, redis, etc.
dubbo protocol is recommended.
dubbo protocol adopts single long connection and NIO asynchronous communication, which is suitable for service calls with small amount of data and large concurrency, and the number of service consumer machines is much larger than that of service provider machines. It is not suitable for services that transmit large amounts of data, such as files and videos, unless the request volume is very low.
Multiple protocols can also be configured in the same project. Different services can use different protocols, for example:
6.1 XML mode

<!-- Multi protocol configuration -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- use dubbo Protocol exposure service -->
<dubbo:service interface="com.lxs.api.HelloService" ref="helloService" protocol="dubbo" />
<!-- use rmi Protocol exposure service -->
<dubbo:service interface="com.lxs.api.DemoService" ref="demoService" protocol="rmi" />

6.2 annotation method

//Configure the agreement on the service provider side
@Service(prorocol = "dubbo")
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "hello " + name;
    }
}

6.3 load balancing

Load Balance: in fact, requests are allocated to multiple operating units for execution, so as to complete work tasks together.
In cluster load balancing, Dubbo provides a variety of balancing strategies (including random, polling, minimum number of active calls, consistency Hash). Random calls are the default.
The load balancing policy can be configured either on the service provider side or on the service consumer side, as follows:

@Controller
@RequestMapping("/demo")
public class HelloController {
    //Configure the load balancing policy on the service consumer side
    @Reference(check = false,loadbalance = "random")
    private HelloService helloService;
    @RequestMapping("/hello")
    @ResponseBody
    public String getName(String name){
        //Remote call
        String result = helloService.sayHello(name);
        System.out.println(result);
        return result;
    }
}

//Configure load balancing on the service provider side
@Service(loadbalance = "random")
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "hello " + name;
    }
}

You can observe the effect of Dubbo load balancing by starting multiple service providers.
Note: since we start multiple service providers on one machine, we need to modify the port number of tomcat and the end of Dubbo service slogan to prevent port conflict.
In the actual production environment, multiple service providers are deployed on different machines, so there is no port conflict.

7. Solve the problem that Dubbo cannot publish the Service proxied by the transaction

We have completed the introduction case of Dubbo. Through the introduction case, we can see that the package can be scanned through the label configuration provided by Dubbo, and the classes scanned to the @ Service annotation can be published as services.
However, if we add the @ Transactional transaction control annotation to the service provider class, the service will not be published successfully. The reason is that the underlying principle of transaction control is to create a proxy object for the service provider class. By default, Spring creates a proxy object based on JDK dynamic proxy, and the complete class name of this proxy object is com sun. proxy.$ Proxy42 (the last two digits are not fixed), which makes Dubbo unable to complete the package matching before publishing the service, so it does not publish the service.

7.1 problem display

The service provider dubbodemo in the entry case_ Display based on the provider project.

  1. In POM Add maven coordinates to the XML file
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.47</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.1.6</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.2</version>
</dependency>

  1. In ApplicationContext service Add data source, transaction manager and relevant configurations for opening transaction annotation in XML configuration file
<!--data source-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroymethod="close">
    <property name="username" value="root" />
    <property name="password" value="root" />
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/test" />
</bean>
<!-- Transaction manager -->
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--Enable annotation support for transaction control-->
<tx:annotation-driven transaction-manager="transactionManager"/>

The database connected above can be created by yourself
3) Add the @ Transactional annotation to the HelloServiceImpl class
4) Start the service provider and service consumer, and access

The above error is that no service provider is available
Check the dubbo management console and find that the service is not published, as shown below:

You can view Dubbo's execution process through breakpoint debugging. Dubbo uses the annotation bean's
The postProcessAfterInitialization method

7.2 solutions

Through the above breakpoint debugging, we can see that after adding transaction annotations to the HelloServiceImpl class, Spring will create a proxy object for this class based on JDK dynamic proxy technology, and the complete class name of the created proxy object is com sun. proxy.$ Proxy35, resulting in Dubbo's failure in package matching (because the package we scanned when publishing the service was com.lxs.service), so the code that actually publishes the service later was not executed.

Solution steps:

  1. Modify ApplicationContext Service XML configuration file, specify the proxy target class attribute when enabling transaction control annotation support, and the value is true. Its function is to use cglib proxy method to create proxy object for Service class.
<!--Enable annotation support for transaction control-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-targetclass="true"/>


2) Modify the HelloServiceImpl class, add the interfaceClass attribute in the Service annotation, and the value is helloservice Class to specify the interface type of the Service.

@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return "hello " + name;
    }
}

This must also be modified. Otherwise, the published service interface will be SpringProxy instead of HelloService interface, as follows:

Topics: Apache Distribution rpc