Spring Cloud -- Spring Cloud microservice practice

Posted by PromaneX on Mon, 01 Jun 2020 05:04:19 +0200

1, Service provider and service consumer

Using microservices to build distributed system, microservices communicate with each other through network. We use service providers and service consumers to describe the invocation relationship between microservices.
noun definition
Service provider The callee of the service (that is, the service that provides services to other services)
Service consumers The caller of the service (that is, the service that depends on other services)

2, Write service provider

The service can query user information through the primary key. In order to facilitate testing, Spring Data JPA is used as the persistence layer framework and H2 is used as the database.
First, create a Maven project

2.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.yuxx.cloud</groupId>
    <artifactId>microservice-simple-provider-user</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!--Spring Boot-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--provide Spring MVC Support for-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--provide Spring Data JPA Support for-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
    </dependencies>

    <!--introduce Spring Cloud Dependence on-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!--add to spring-boot Of maven plug-in unit-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.2 table creation statement

Build under the classpath of the project schema.sql , and add the following:
drop table user if exists;
create table user(
    id bigint generated by default  as identity,
    username varchar(40),
    name varchar (20),
    age int(3),
    balance decimal (10,2),
    primary key(id)
);

2.3 table data

Build under the classpath of the project data.sql , and add the following:
insert into user(id,username,name,age,balance) values(1,'account1','Zhang San',20,100.00);
insert into user(id,username,name,age,balance) values(2,'account2','Li Si',28,180.00);
insert into user(id,username,name,age,balance) values(3,'account3','Wang Wu',32,280.00);

2.4 user entity class

package com.yuxx.cloud.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import javax.persistence.*;
import java.math.BigDecimal;

@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" })
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String username;
    @Column
    private String name;
    @Column
    private Integer age;
    @Column
    private BigDecimal balance;

    //...Setter and Getter
}

2.5 DAO

package com.yuxx.cloud.dao;

import com.yuxx.cloud.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User,Long> {
}

2.6 Controller

package com.yuxx.cloud.controller;

import com.yuxx.cloud.dao.UserRepository;
import com.yuxx.cloud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id){
        User user = userRepository.getOne(id);
        return user;
    }
}
GetMapping is a new annotation provided by Spring 4.3. It is a composite annotation, equivalent to @ requestmapping (method= RequestMethod.GET ), to simplify development. At the same time, @ PostMapping, @ PutMapping, @ DeleteMapping, @ PatchMapping, etc.

2.7 startup

package com.yuxx.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProviderUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderUserApplication.class,args);
    }
}

2.8 configuration file

The properties format file is used as the configuration file in traditional Web development. Spring Boot and Spring Cloud support using properties or yml format files as configuration files.
yml file format is a file format written by YAML, and YAML and properties files can be converted to each other. YAML has a clearer structure than properties; it is more readable and maintainable, and its syntax is very concise.
Our case uses YAML as a configuration file, yml has strict indentation, and the key and value are separated by ":". The space after the colon cannot be less.

2.9 testing

Browser access http://localhost:8000/1, results obtained:

    

3, Write service consumer

The service is very simple. It uses RestTemplate to call the API of user microservice to query the user information of the specified ID.
First, create a maven project

3.1 pom file

The content is basically the same as the pom file of the user's microservice:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yuxx.cloud</groupId>
    <artifactId>microservice-simple-consumer-movie</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!--Spring Boot-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
    </dependencies>

    <!--introduce Spring Cloud Dependence on-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!--add to spring-boot Of maven plug-in unit-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2 user entity class

A POJO does not need those annotations in the user's microservice.
package com.yuxx.cloud.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import javax.persistence.*;
import java.math.BigDecimal;

public class User {
    private Long id;

    private String username;

    private String name;

    private Integer age;

    private BigDecimal balance;

    //Setter and Getter
}

3.3 startup

package com.yuxx.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumerMovieApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

3.4 Controller

The API in which the RestTemplate is used to request the user's microservice.
package com.yuxx.cloud.controller;

import com.yuxx.cloud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class MovieController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("user/{id}")
    public User findById(@PathVariable Long id){
        return restTemplate.getForObject("http://localhost:8000/" + id,User.class);
    }
}

3.5 configuration file

server:
  port: 8010

3.6 test visit

    

4, Integrating spring boot activator

Spring Boot comes with its own monitoring function, the activator, which can help to monitor the internal operation of the program, such as monitoring status, Bean loading status, environment variables, log information, thread information, etc.
     Spring Boot  Activator provides many monitoring endpoints, which can be used http://{ip}:{port}/{endpoint} to access these endpoints.
The endpoint provided by the Actuator is shown in the following table:
Endpoint describe HTTP method Sensitive or not
autoconfig Display auto configuration information GET yes
beans Show all spring beans in the application context GET  
configprops Display the list of configuration properties for all @ ConfigurationProperties GET yes
dump Show a snapshot of thread activity GET yes
env Display the environment variables of the application GET yes
health Displays the health indicators for the application, which are provided by the health indicator's implementation class. When the application turns on security protection, only simple status will be displayed for the request without user authentication; if it is authenticated, the health details will be displayed. GET no
info Display the application information. You can use the info. * property to customize the data exposed by the info endpoint GET no
mappings Display the path list of all @ RequestMapping GET yes
metrics Show applied metrics information GET yes
shutdown Close the application (not enabled by default. If you want to enable it, you need to set end-points.shutdown.enabled=true ) POST yes
trace Display trace information (100 HTTP requests by default) GET yes
Take microservice simple provider user as an example, add the following dependencies:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
It's easy to integrate the activator.
Spring boot version 2. X, discarding the configuration writing method of the previous version 1.x. For security, the default actor exposes only two endpoints: health and info, and The health endpoint can only view the summary by default.
Test 1. / health endpoint
    
At this point, you can show the health of the application. Among them, UP means normal operation, in addition to UP, there are DOWN and OUT_OF_SERVICE, UNKOWN, etc.
(2) Exposed end point
In Spring Boot 1.x, you can expose all endpoints and view detailed data through the following configuration:
management:
  security:
    enabled: false

In Spring Boot 2.x, activator only opens the health and info endpoints by default. 1.x view detailed data configuration mode is deprecated. To view the detailed data of health port, you can configure it as follows:

management:
  endpoint:
    health:
      show-details: always

Access via browser: http://localhost : 8000 / Actor / health, or the result is similar to the following:

    

The essence of / health is to check the resources of the Spring Boot application to determine whether the application is normal.
Test 2. / info endpoint

Visit: http://localhost:8000/actuator/info , you can see the following:

    

It can be seen from the results that the info port did not return any data to us. You can use the info. * property to define the data exposed by the info port:

info:
  app:
    name: @project.artifactId@
    encoding: @project.build.sourceEncoding@
    java:
      source: @java.version@
      target: @java.version@

After restart, visit again: http://localhost:8000/actuator/info , you will see something similar to the following:

    

    

The result shows that the info endpoint returns project name, code, Java version and other information.
There are many activator endpoints, and Spring Boot 2.x is different from 1.x. please refer to the Spring Boot documentation to test other endpoints by yourself. I won't elaborate here.

5, What are the problems of hard coding

In movicontroller, we hardcode the network address (IP, port, etc.) of the network provider in the code, and of course, we can also extract it into the configuration file, for example:
user:
  userServiceUrl: http://localhost:8000/

The movicontroller code is changed to:

@Value("${user.userServiceUrl}")
private String userServiceUrl;

@GetMapping("user/{id}")
public User findById(@PathVariable Long id){
    return restTemplate.getForObject(userServiceUrl + id,User.class);
}
In traditional applications, this is generally done, but there are problems in this way:
  • Limited use scenarios
    If the network address (IP and port) of the service provider changes, it will affect the service consumer. For example, if the network address of the user's microservice changes, it is necessary to modify the configuration of the movie microservice and republish it, which is obviously not desirable.
  • Unable to scale dynamically
    In the production environment, each microservice will generally deploy multiple instances to achieve disaster recovery and load balancing. In the system of microservice architecture, it also needs the ability of automatic scaling, such as dynamic increase and decrease of nodes. Hard coding can't meet this demand.
So how to solve these problems? Microservice registration and discovery

Topics: Programming Spring Maven Java Apache