Introduction to Java architecture and dubbo

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:

The structure is simple, the early development cost is low and the development cycle is short, which is 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 structure

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.

System expansion can only be achieved 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 the operating system process in a separate form.

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, 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 extracting services 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.

Applicable to the Internet era, 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 a high-performance and lightweight open-source Java RPC framework opened by Alibaba, which can be seamlessly integrated with the Spring framework.

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 method 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 semantics of the call and convey the data of the call 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 provides three core capabilities: interface oriented remote method invocation, intelligent fault tolerance and load balancing, and automatic service registration and discovery.

2.2 Dubbo architecture


Node role description:

node Role name
Provider Service provider of exposed services
Consumer The service consumer that invokes the remote service
Registry Registry for service registration and discovery
Monitor The monitoring center that counts the number and time of service calls
Container Service run container

The dotted line is asynchronous access, and the solid line is synchronous access
Blue dotted line: functions completed at startup
The red dotted line (solid line) is the function executed 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. Service consumers subscribe to the services they need from the registry when they start.
  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 in it. 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 registration center of Dubbo service. It has high industrial intensity, can be used in production environment, and is recommended.
Zookeeper tree directory service:

Process Description:

  • When a service provider starts up: to / Dubbo / com foo. Write your own URL address in the barservice / providers directory
  • When a service consumer starts: subscribe to / 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 version of Zookeeper used in this course is 3.4.6. After downloading, you can get the name Zookeeper-3.4.6 tar. GZ compressed file.

Installation steps:

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

3.3 start and stop Zookeeper

Enter the bin directory of Zookeeper and start the service command
./zkServer.sh start

Stop service command
./zkServer.sh stop

View service status:
./zkServer.sh status

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

Development steps:

(1) Create maven project (packaged as war) dubbodemo_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>

(2) Configure 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>

(3) Create service interface

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

(4) Create service implementation class

@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

(5) Create ApplicationContext service 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_provider" />
	<!-- Connect service registry zookeeper ip by zookeeper Server ip address-->
	<dubbo:registry address="zookeeper://192.168.134.129:2181"/>
	<!-- Registration agreement and port   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.jerry.service.impl" />
</beans>

(6) Start service

tomcat7:run

4.2 service consumer development

Development steps:

(1) Create maven project (packaged as war) dubbodemo_consumer,pom. The XML configuration is the same as that of the above service provider. You only need to change the port number of Tomcat plug-in to 8082

(2) Configure 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 loaded configuration file 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>*.do</url-pattern>
  </servlet-mapping>
</web-app>

(3) Copy the HelloService interface in the service provider project to the current project

(4) Write Controller

@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

(5) 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.Jerry.controller" />
</beans>

(6) Run test

tomcat7:run start

Enter in the browser http://localhost:8082/demo/hello.do?name=Jack , view browser output

Think 1: in the above introduction to Dubbo case, we copied the HelloService interface from the service provider project to the service consumer project. 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 this 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 achieve remote invocation?

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, the bottom layer of Dubbo's network transmission is based on the Netty framework.

Thinking 3: in the Dubbo introduction case above, we use Zookeeper as the service registration center. 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 the cluster mode. Zookeeper clusters can be configured to achieve high availability of zookeeper services and prevent single point of failure.

5. Configuration description of Dubbo

5.1 packet scanning

<dubbo:annotation package="com.jerry.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.jerry.service.impl.HelloServiceImpl" />
<dubbo:service interface="com.jerry.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.jerry.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.

5.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:

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

5.3 inspection during startup

<dubbo:consumer check="false"/>

The above configuration needs to be configured on the service consumer side. If it is not configured, the default check value is true. By default, Dubbo will check whether the dependent services are available at startup. When unavailable, an exception will be thrown to prevent the completion of Spring initialization, so that problems can be found as soon as possible when online. You can turn off the check by changing the check value to false.

It is recommended to set the check value to false in the development stage and to true in the production environment.

5.4 load balancing

Load Balance: in fact, it is to allocate requests 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), and the default is random random calls.

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 Dubbo service to prevent port conflict.

In the actual production environment, multiple service providers are deployed on different machines, so there is no port conflict.

6. Solve the problem that Dubbo cannot publish the Service represented by the transaction

Previously, 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 @ 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, and then does not publish the service.

6.1 problem display

Dubbodemo, the service provider in the entry case_ Display based on provider project

Operation steps:

(1) In POM Add maven coordinates in 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>

(2) In ApplicationContext service Add relevant configurations of data source, transaction manager and transaction annotation in XML configuration file

<!--data source-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="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 @ Transactional annotation on HelloServiceImpl class

(4) Start the service provider and service consumer, and access

6.2 solutions

Through the above breakpoint debugging, we can see that after adding transaction annotation to 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.itheima.service), so the code that actually publishes the service later was not executed.

Solution steps:

(1) Modify ApplicationContext Service XML configuration file. When the transaction control annotation support is enabled, specify the proxy target class attribute 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-target-class="true"/>

(2) Modify the HelloServiceImpl class, add the interfaceClass attribute in the Service annotation, and the value is helloservice Class, which is used 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;
    }
}

Common errors and Solutions

Tags: Spring

Posted by Xproterg^vi on Wed, 04 May 2022 09:36:50 +0300