Spring Cloud learning notes

Why learn Spring Cloud

In project development, with more and more businesses, it leads to high coupling between functions, low development efficiency, slow system operation, difficult to maintain and unstable. Microservice architecture can solve these problems, and Spring Cloud is the most popular implementation of microservice architecture, so let's learn about Spring Cloud today.

1. System architecture evolution

With the development of the Internet, the scale of website applications continues to expand and the demand surges, followed by technical pressure. Therefore, the system architecture continues to evolve, upgrade and iterate. From single application, to vertical splitting, to distributed services, to SOA, and now hot micro service architecture.

1.1. Centralized architecture

When the website traffic is small, only one application is needed to deploy all functions together to reduce deployment nodes and costs.


advantage:

  • Fast system development
  • Low maintenance cost
  • It is suitable for systems with low concurrency requirements

Disadvantages:

  • High code coupling and difficult later maintenance
  • Cannot optimize for different modules
  • Cannot expand horizontally
  • Low single point fault tolerance and poor concurrency

1.2. Vertical split

When the number of visits increases gradually and a single application cannot meet the requirements, in order to meet higher concurrency and business requirements, we split the system according to business functions:


advantage:

  • The system split realizes traffic sharing and solves the problem of concurrency
  • It can be optimized for different modules
  • Convenient horizontal expansion, load balancing and improved fault tolerance

Disadvantages:

  • The systems are independent of each other, and there will be a lot of repeated development work, which will affect the development efficiency

1.3. Distributed services

When there are more and more vertical applications, the interaction between applications is inevitable. Extract the core business as an independent service, and gradually form a stable service center, so that the front-end applications can respond to the changing market demand more quickly. At this time, distributed invocation for improving business reuse and integration is the key.


advantage:

  • The basic services are extracted, and the systems call each other, which improves the code reuse and development efficiency

Disadvantages:

  • The coupling between systems becomes higher, and the call relationship is complex and difficult to maintain

1.4. Service governance (SOA)

SOA (Service Oriented Architecture): it is a design method, which contains multiple services. Services eventually provide a series of functions through interdependence. A service usually exists in an independent form in the operating system process. Each service is called through the network.


Disadvantages of SOA: there are deviations in ESB products provided by each supplier, and its own implementation is complex; The granularity of application services is large, and ESB integration integrates all services, protocols and data conversion, making operation and maintenance, test and deployment difficult. All services communicate through one channel, which directly reduces the communication speed.

1.5. Microservices

Microservice architecture is a way or way to use a set of small services to develop a single application. Each service is built based on a single business capability, runs in its own process, communicates using lightweight mechanisms, usually HTTP API s, and can be deployed independently through automatic deployment mechanisms. These services can be implemented in different programming languages and different data storage technologies, and maintain a minimum of centralized management. Microservice structure diagram:


Characteristics of microservices:

  • Single responsibility: each service in the micro service corresponds to a unique business capability to achieve a single responsibility
  • Service oriented: service oriented means that each service should expose the service interface API. It doesn't care about the technical implementation of the service. It has nothing to do with the platform and language, and it is not limited to what technology to implement, as long as the REST interface is provided.
  • Autonomy: autonomy means that services are independent and do not interfere with each other
    1. Team independence: each service is an independent development team.
    2. Technology independence: because it is service-oriented and provides REST interfaces, there is no interference with the technology used
    3. Front and rear end separation: the front and rear end separation development is adopted to provide a unified REST interface, and the back end does not need to develop different interfaces for PC and mobile section
    4. Database separation: each service uses its own data source

Comparison between microservices and SOA:

functionSOAMicroservices
Component sizeBlock business logicIndividual tasks or small pieces of business logic
couplingUsually loosely coupledAlways loose coupling
AdministrationFocus on central managementFocus on decentralized management
targetEnsure that applications can interoperateEasy to maintain, easy to expand, lighter interaction

2. Remote call mode

Both microservices and SOA are faced with remote invocation between services. So what are the remote invocation methods between services?

Common remote calling methods are as follows:

  • RPC: Remote Procedure Call, similar to RMI. Custom data format, based on native TCP communication, fast and efficient. Early web services, now popular Dubbo, are typical of RPC.
  • Http: http is actually a network transmission protocol, which specifies the format of data transmission based on TCP. At present, the communication between client browser and server basically adopts HTTP protocol. It can also be used for remote service calls. The disadvantage is that the message encapsulation is bloated. Now the popular REST style can be realized through HTTP protocol.

2.1. Understanding RPC

RPC, or Remote Procedure Call, is A computer communication protocol. The protocol allows A program running on one computer to call A subroutine of another computer without extra programming for this interaction. To put it more popularly, computer A provides A service, and computer B can call the service of computer A like calling A local service.

RPC call flow chart:

2.2. Understanding HTTP

HTTP is actually a network transmission protocol. It works at the application layer based on TCP and specifies the format of data transmission. Now the communication between the client browser and the server basically adopts HTTP protocol, which can also be used for remote service calls. The disadvantage is that the message encapsulation is bloated. The advantage is that there are no technical restrictions on the service provider and caller. It is free and flexible, which is more in line with the concept of microservice. Now the popular REST style can be realized through HTTP protocol.

2.3. How to select?

The mechanism of RPC is defined according to the language API, not according to the network-based application. If your company adopts Java technology stack, it is a good choice to use Dubbo as micro service architecture.

On the contrary, if the company's technology stack is diversified and you prefer the spring family, Spring Cloud is the best choice to build microservices. Spring Cloud suite will be selected, so HTTP will be used to implement inter service calls.

3. Introduction to spring cloud

3.1. Introduction

Spring Cloud is one of the projects under spring, Click to enter the official website address

Spring is best at integration, taking the best framework in the world and integrating it into its own project.

Spring Cloud is the same. It integrates some popular technologies and realizes functions such as configuration management, service discovery, intelligent routing, load balancing, fuse, control bus, cluster status and so on. The main components involved include:

Netflix

  • Eureka: Registration Center
  • Zuul: service gateway
  • Ribbon: load balancing
  • Feign: service call
  • Hystrix: fuse

The above is only a part of it. The architecture diagram is as follows:

3.2. Version

The naming of Spring Cloud is special because it is not A component, but A collection of many components. Its name is composed of some words with letters A to Z (actually the name of London Underground Station):

Correspondence between spring clone and Spring Boot versions

Release TrainBoot Version
Hoxton2.2.x
Greenwich2.1.x
Finchley2.0.x
Edgware1.5.x
Dalston1.5.x

4. Microservice scenario simulation

Simulate a service invocation scenario. Easy to learn later courses

4.1. Create parent project

In microservices, multiple projects need to be created at the same time. In order to facilitate classroom demonstration, first create a parent project, and subsequent projects take this project as the parent, using Maven's aggregation and inheritance. Uniformly manage the version and configuration of subprojects


pom.xml file:

<?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>com.lxs</groupId>
    <artifactId>lxs-springcloud</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        <mapper.starter.version>2.1.5</mapper.starter.version>
        <mysql.version>5.1.46</mysql.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- currency Mapper starter -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>${mapper.starter.version}</version>
            </dependency>
            <!-- mysql drive -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
be careful: spring clound and spring boot Corresponding version of greenwich edition clound corresponding spring boot 2.1.x
 Note: aggregate parent project <packaging>pom</packaging>

Most of the dependent versions to be used have been managed here to facilitate subsequent use

4.2. Service providers

We will create a new project to provide external services for querying users.

4.2.1. Create Module

Select LXS spring cloud to create a sub project:

pom.xml file

<?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">
    <parent>
        <artifactId>lxs-springcloud</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- currency Mapper starter -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
        </dependency>
        <!-- mysql drive -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-bus</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

    </dependencies>
</project>

Project structure

4.2.2. Preparation of configuration file

Create the user service \ SRC \ main \ resources \ application.yml property file. Here, we use yaml syntax instead of properties:

server: 
	port: 9091
spring: 
	datasource: 
		driver-class-name: com.mysql.jdbc.Dri
		url: jdbc:mysql://localhost:3306/springclound
		username: root
		password: root
mybatis: 
	type-aliases-package: com.lxs.user.pojo

Use the Mysql graphical interface tool to import the springcloud.sql script

4.2.3. Code writing

Startup class

@SpringBootApplication 
@MapperScan("com.lxs.user.mapper")
public class UserApplication { 
	public static void main(String[] args) {
		SpringApplication.run(UserApplication.class, args); 
	} 
}

Entity class

@Data
@Table(name = "tb_user")
public class User{
    // id
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // user name
    private String userName;

    // password
    private String password;

    // full name
    private String name;

    // Age
    private Integer age;

    // Gender, 1 male, 2 female
    private Integer sex;

    // date of birth
    private Date birthday;

    // Creation time
    private Date created;

    // Update time
    private Date updated;

    // remarks
    private String note;
}

UserMapper

public interface UserMapper extends Mapper<User> { 

}

service:

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) {
        return userMapper.selectByPrimaryKey(id);
    }
}

controller

Provide REST style web Services externally, and query users according to their id

@RestController
@RequestMapping("/user")
@RefreshScope //Profile change auto refresh attribute
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id) {
        return userService.queryById(id);
    }
}

Project structure after completing the above code

4.2.4. Start and test

Start project, access http://localhost:9091/user/7

4.3. Service callers

4.3.1. Create project

Similar to the above, we won't repeat it here. It should be noted that we can call the user service, so we don't need Mybatis. We rely on the user service module before copying to change the coordinates of the response

pom:

<?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">
    <parent>
        <artifactId>lxs-springcloud</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer-demo</artifactId>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

    </dependencies>

</project>

The project structure is as follows:

4.3.2. Code writing

Starter:

@SpringCloudApplication
@EnableFeignClients //Enable feign function
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

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

Spring provides a RestTemplate template tool class, encapsulates the HTTP based client, and implements the serialization and deserialization of objects and json, which is very convenient. RestTemplate does not limit the HTTP client type, but abstracts it. Currently, three commonly used types are supported:

  • HTTPClient
  • OkHTTP
  • JDK native URLConnection (default)

Entity class

@Data
public class User {

    // id
    private Long id;

    // user name
    private String userName;

    // password
    private String password;

    // full name
    private String name;

    // Age
    private Integer age;

    // Gender, 1 male, 2 female
    private Integer sex;

    // date of birth
    private Date birthday;

    // Creation time
    private Date created;

    // Update time
    private Date updated;

    // remarks
    private String note;
}

controller

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/{id}")
    public String queryById(@PathVariable Long id) {
        String url = "HTTP://localhost:9091/user/" + id;
        return restTemplate.getForObject(url, String.class);
    }
}

4.3.3. Start up test:

Because we do not configure ports, the default is 8080. We visit: http://localhost:8080/consume/7

A simple remote service invocation case is implemented.

5.4. Thinking

Briefly review what we just wrote:

  • User service: provides an external interface for querying users
  • Consumer Demo: accessed through RestTemplate http://locahost:9091/user/ {id} interface, what is the problem with querying user data?
  • In the consumer, we hard code the url address into the code, which is not convenient for later maintenance
  • The consumer needs to remember the address of the user service. If there is a change, it may not be notified, and the address will become invalid
  • The consumer does not know the status of the user service, nor does it know that the service is down
  • There is only one user service, which is not highly available
  • Even if user services form clusters, consumer s need to achieve load balancing by themselves

In fact, the problems mentioned above are the problems that distributed services must face:

  • Service management
    1. How to automatically register and discover
    2. How to realize status supervision
    3. How to implement dynamic routing
  • How do services achieve load balancing
  • How does the service solve the problem of disaster recovery
  • How to realize unified configuration of services

All the above questions will be answered in spring cloud.

6.Eureka registry

6.1 introduction to Eureka

problem analysis

In the case just now, the user service needs to expose its address when providing services externally. The consumer (caller) needs to record the address of the service provider. If the address changes in the future, it needs to be updated in time. This doesn't seem to matter when there are few services, but in today's increasingly complex Internet environment, a project will certainly split into more than a dozen or even dozens of micro services. At this time, if you still manage the address artificially, it will not only be difficult to develop, but also very troublesome to test, release and go online in the future, which runs counter to the idea of DevOps.

Online car Hailing

This is like before the emergence of online car hailing, people can only call a taxi when they go out to call a car. Some private cars want to rent but are not qualified. They are called black cars. Many people want to make an appointment, but there are too few taxis, which is inconvenient. There are a lot of private cars, but they dare not stop, and there are cars all over the street. Who knows which one is willing to carry people. One wants and the other is willing to give, which is the lack of introduction and management.

At this time, an online car Hailing platform like didi appears. All private cars that want to carry passengers register with didi and record your model (service type) and identity information (contact information). Such private cars can be found in didi at a glance.

At this time, the person who wants to call a car only needs to open the APP, enter your destination, select the model (service type), and didi will automatically arrange a car that meets the needs to serve you. Perfect!

What does Eureka do?

Eureka is like Didi, responsible for managing and recording the information of service providers. Service callers do not need to find services themselves, but tell Eureka their needs, and Eureka will tell you the services that meet your needs.

At the same time, the service provider and Eureka are monitored through the "heartbeat" mechanism. When a service provider has a problem, Eureka will naturally remove it from the service list.

This realizes the automatic registration, discovery and status monitoring of services.

6.2. Schematic diagram

Basic architecture:

  • Eureka: it's a service registry (which can be a cluster) that exposes its own address
  • Provider: register your information with Eureka after startup (address, what services to provide)
  • Consumer: subscribe to the service from Eureka. Eureka will send the address list of all providers of the corresponding service to consumers and update it regularly
  • Heartbeat (renewal): the provider periodically updates its status to Eureka through HTTP

6.3. Introduction cases

6.3.1. Write EurekaServer

Eureka is a service registration center, which only does service registration; It does not provide services or consume services. You can build Web projects using Eureka or Spring Boot.

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">
    <parent>
        <artifactId>lxs-springcloud</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>

Write startup class:

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

Write configuration:

server:
  port: 10086
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
      # The address of the eureka service. For clustering, you need to specify other eureka addresses
      defaultZone: http://127.0.0.1:10086/eureka
    #Don't register yourself
    register-with-eureka: false
    #No pull service
    fetch-registry: false

Start the service and access: http://127.0.0.1:10086/

6.3.2. Service registration

Add Eureka client dependency on the service providing project user service; Automatically register the service with EurekaServer service address list.

Add dependency

<!-- Eureka client -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Enable Eureka client function on startup class

Enable Eureka client functionality by adding @ EnableDiscoveryClient

@SpringBootApplication
@MapperScan("com.lxs.user.mapper")
@EnableDiscoveryClient //Enable Eureka client discovery
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

Write configuration

server:
	port: 9091
Srping:
	datasource: 
		driver-class-name: com.mysql.jdbc.Driver
		url: jdbc:mysql://localhost:3306/springcloud
		username: root
		password: root
	application:
		name: user-service
mybatis:
	type-aliases-package: com.lxs.user.pojo
eureka:
	client:
	service-url:
		defaultZone: HTTP://127.0.0.1:10086/eureka

be careful:

  • Here, we added the spring.application.name attribute to specify the application name, which will be used as the application id in the future.
  • Do not specify register with Eureka and fetch registry, because the default is true

Restart project, access http://127.0.0.1:10086/eureka Page view


We found that the user service service has been registered successfully

6.3.3. Service discovery

Add Eureka client dependency on the consumer demo of the service consumption project; You can use the tool class DiscoveryClient to obtain the corresponding service address list according to the service name.

1) Add dependency:

<!-- Eureka client -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2) Add the annotation to start Eureka client discovery in the startup class

@SpringBootApplication
@EnableDiscoveryClient //Enable Eureka client discovery
@EnableCircuitBreaker //Open fuse
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

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

3) Modify configuration:

spring:
  application:
    name: consumer-demo
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

4) Modify the code and use the method of DiscoveryClient class to obtain the service instance according to the service name:

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/{id}")
    public String queryById(@PathVariable Long id) {
        String url = "http://localhost:9091/user/" + id;

		List<ServiceInstance> serviceInstances = discoveryClient.getInstances("user-service");
		ServiceInstance serviceInstance = serviceInstances.get(0);
		url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/" + id;
		
        return restTemplate.getForObject(url, User.class);
    }
}

5) Debug trace run:

Generated URL:


Access results:

6.4. Detailed explanation of Eureka

Next, we will explain the principle and configuration of Eureka in detail.

6.4.1. Infrastructure

Three core roles in Eureka architecture:

  • Service registry
    Eureka's server application, which provides service registration and discovery functions, is the Eureka server we just established
  • Service provider
    The applications that provide services can be Spring Boot applications or any other technology implementation, as long as they provide REST style services. In this example, we implement the user service
  • Service consumers
    The consumer application obtains the service list from the registry, so as to know the information of each service provider and where to call the service provider. In this example, we implement the consumer demo

6.4.2. Highly available Eureka Server

Eureka Server is the service registry. In the case just now, we only have one Eureka Server. In fact, Eureka Server can also be a cluster to form a highly available Eureka center.

Eureka Server is a web application that can start multiple instances (configure different ports) to ensure high availability of Eureka Server.

Service synchronization

Multiple Eureka servers will also register as services. When the service provider registers with a node in the Eureka Server cluster, the node will synchronize the service information to each node in the cluster, so as to realize data synchronization. Therefore, no matter the client accesses any node in the Eureka Server cluster, it can obtain the complete service list information.

As a client, you need to register the information in each Eureka

If there are three Eureka servers, each Eureka server needs to register with several other Eureka services.

For example, if three are 10086, 10087 and 10088 respectively, then:

  • 10086 to register on 10087 and 10088
  • 10087 to register on 10086 and 10088
  • 10088 to register on 10086 and 10087

Build a highly available Eureka server

Suppose we want to build two Eureka server clusters with ports 10086 and 10087 respectively

1) We modified the original EurekaServer configuration:

server: 
	port: ${port:10086}
spring:
	application:
		name: eureka-server
eureka: 
	client:
		#The service address of eureka. If it is a cluster, you need to specify the eureka address of other clusters
		service-url:
			defaultZone: ${defaultZone:http://127.0.0.1:10086/eureka}
			# Don't register yourself 
			#register-with-eureka: false 
			# Do not grab service 
			#fetch-registry: false

The so-called high availability registry actually registers EurekaServer itself as a service, so that multiple eurekaservers can find each other and form a cluster. Therefore, we have made the following modifications:

  • The two configurations register with Eureka = false and fetch registry = false are deleted. Because the default value is true, you will register yourself in the registry.
  • Change the value of the service URL to the address of another Eureka server instead of your own

2) The other can specify port and defaultZone configuration when starting:

Modify the original startup configuration component; In VM options in the following interface
Setting - DdefaultZone=http:127.0.0.1:10087/eureka

Make a copy and modify it; Set - Dport=10087 - DdefaultZone=http:127.0.0.1:10086/eureka in VM options in the following interface


3) Start up test; Start two Eureka servers at the same time

4) The client registers the service to the cluster
Since there is more than one EurekaServer, the service URL parameter needs to be changed when registering the service:

eureka: 
	client: 
		service-url: # EurekaServer address. Multiple addresses are separated by ',' 		
			defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka

In order to facilitate the class and the following content modification, you can change back to a single eureka server after testing the above configuration.

6.4.3.Eureka client and server configuration

In this section, we perform a series of configurations:


The service provider should register the service with EurekaServer and complete service renewal.

Service registration

When the service provider starts, it will check whether the parameter Eureka. Client. Register with erueka = true in the configuration attribute is true. In fact, the default is true. If the value is true, it will send a Rest request to EurekaServer and carry its own metadata information. EurekaServer will save these information in a double-layer Map structure.

  • The Key of the first layer Map is the service id, which is generally the spring.application.name attribute and user service in the configuration
  • The key of the second layer Map is the instance id of the service. General host+ serviceId + port, for example: localhost: User Service: 8081
  • Value is the instance object of the service, that is, a service. In this way, multiple different instances can be started at the same time to form a cluster.

By default, the host name or localhost is used for registration. If you want to register with ip, you can add the following configuration in user service:

eureka: 
	instance: 
		ip-address: 127.0.0.1 # ip address
		prefer-ip-address: true # Prefer to use ip rather than host name

After modification, restart user service and consumer demo successively; It becomes an ip address when calling the service; It should be noted that the console service instance status is not displayed in eureka.

Service renewal

After the registration service is completed, the service provider will maintain a heartbeat (regularly send a Rest request to EurekaServer) and tell EurekaServer: "I'm still alive". This is what we call service renew al;

There are two important parameters that can modify the behavior of service renewal; You can add the following configuration items in user service:

eureka: 
	instance: 
		lease-expiration-duration-in-seconds: 90
		lease-renewal-interval-in-seconds: 30
  • Lease renewal interval in seconds: the interval of service renewal. The default is 30 seconds
  • Lease expiration duration in seconds: the default value is 90 seconds

In other words, by default, the service will send a heartbeat to the registry every 30 seconds to prove that it is still alive. If no heartbeat is sent for more than 90 seconds, EurekaServer will consider the service down and remove it from the service list. These two values need not be modified in the production environment, but can be modified by default.

Get service list

When the service consumer starts, it will detect the value of the Eureka. Client. Fetch registry = true parameter. If it is true, it will pull a read-only backup from the list of Eureka Server services and cache it locally. And the data will be pulled and updated every 30 seconds. You can modify the following parameters in the consumer demo project

eureka: 
	client: 
		registry-fetch-interval-seconds: 30

In the production environment, we do not need to modify this value.
However, in order to quickly get the latest status of services in the development environment, we can set it smaller.

6.4.5. Failure elimination and self-protection

The following configurations are performed on the Eureka Server server server:

Service offline

When the service is shut down normally, it will trigger a REST request for service offline to Eureka Server and tell the service registry: "I'm going offline". After receiving the request, the service center will set the service to offline status

Failure elimination

Sometimes our service may not work normally due to memory overflow or network failure, and the service registry has not received the request for "service offline". Compared with the "service renewal" operation of the service provider, the service registry will create a scheduled task when starting. By default, the services in the current list that have timed out (90 seconds by default) and have not renewed will be rejected every other period of time (60 seconds by default). This operation is called invalidation rejection. You can modify it through the eureka.server.eviction-interval-timer-in-ms parameter. The unit is milliseconds.

Self protection

When we shut down a service, we will see a warning on the Eureka panel:

This triggered Eureka's self-protection mechanism. When a service fails to renew its heartbeat on time, Eureka will count whether the proportion of service instances with heartbeat failure in the last 15 minutes exceeds 85%. When Eureka server node loses too many clients in a short time (network partition failure may occur). In the production environment, due to network delay and other reasons, the proportion of heartbeat failure instances is likely to exceed the standard, but it is not appropriate to remove the service from the list at this time, because the service may not be down. Eureka will protect the registration information of the current instance and will not eliminate it. This is effective in a production environment, ensuring that most services are still available.

However, this brings trouble to our development, so we will turn off the self-protection mode in the development stage:

eureka: 
	server: 
		enable-self-preservation: false # Turn off self-protection mode (on by default)
		eviction-interval-timer-in-ms: 1000 # Scan the interval between failed services (60*1000ms by default)

Summary:

  • user-service
eureka: 
	client: 
		service-url: 
			defaultZone: http://127.0.0.1:10086/eureka
	instance:
		# Prefer ip addresses to host names
		prefer-ip-address: true
		# ip address
		ip-address: 127.0.0.1
		# Renewal interval: 30 seconds by default
		lease-renewal-interval-in-seconds: 5
		# Service failure time, 90 seconds by default
		lease-expiration-duration-in-seconds: 5
  • consumer-demo
eureka: 
	client: 
		service-url: 
			defaultZone: http://127.0.0.1:10086/eureka
		# The interval between obtaining the service address list is 30 seconds by default
		registry-fetch-interval-seconds: 10
  • eureka-server
eureka: 
	server: 
		# Service failure elimination interval: 60 seconds by default
		eviction-interval-timer-in-ms: 60000
		# Turn off self-protection mode (on by default)
		enable-self-preservation: false

7. Load balancing Ribbon

In the case just now, we started a user service, then obtained the service instance information through the discovery client, and then obtained the ip and port for access.

However, in the actual environment, we often start many user service clusters. At this point, there will be multiple services in the service list. Which one should we access?

Generally, in this case, we need to write a load balancing algorithm to select from multiple instance lists.

However, Eureka has helped us integrate the load balancing component: Ribbon, which can be used simply by modifying the code.

What is Ribbon:

Next, let's use Ribbon to achieve load balancing.

7.1. Start two service instances

First, we start two user service instances, one 9091 and one 9092.

Configure the following ports in user service:

server: 
	port: ${port:9091}

Configure the following in startup configuration

Eureka monitoring panel:

7.2. Turn on load balancing

Because Ribbon has been integrated in Eureka, we do not need to introduce new dependencies. Modify code directly:

Add @ LoadBalanced annotation on the configuration method of RestTemplate:

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

Modify the calling method. Instead of manually obtaining the ip and port, it is called directly through the service name:

@GetMapping("/{id}") 
public User queryById(@PathVariable("id") Long id) {
	String url = "http://user-service/user/" + id;
	return restTemplate.getForObject(url, User.class);
}

Visit the page to view the results:

Perfect!
Visit the page to view the results; You can also view the execution status on the console of 9091 and 9092:

7.3. Source code tracking

Why can you access the service only by entering the service name? Also get the ip and port before.

Obviously, a component obtains the ip and port of the service instance according to the service name. Because the consumer demo uses the RestTemplate, spring uses the LoadBalancerInterceptor interceptor. This class intercepts the RestTemplate request, then obtains the service list from Eureka according to the service id, and then uses the load balancing algorithm to obtain the real service address information and replace the service id.

We track the source code:


Continue to follow the execute method: it is found that the service obtained port 9092

Next time, it is found that 9091 is obtained:

7.4. Load balancing strategy

The default load balancing policy of Ribbon is simple polling. We can test it:

Write a test class. In the source code just now, we see that the RibbonLoadBalanceClient is used for load balancing in interception. There is a choose method, which is introduced as follows:

Now this is the method of load balancing to obtain instances.

We inject the object of this class, and then test it:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = UserConsumerDemoApplication.class)
public class LoadBalanceTest {

    @Autowired
    private RibbonLoadBalancerClient client;

    @Test
    public void test() {
        for (int i=0; i < 100; i++) {
            ServiceInstance instance = client.choose("user-service");
            System.out.println(instance.getHost() + ":" + instance.getPort());
        }
    }

}

result:

It is in line with our expectation and speculation. It is indeed a polling method.

SpringBoot also provides us with a configuration entry to modify load balancing rules:

user-service: 
	ribbon: 
		NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

The format is: {service name}. ribbon.NFLoadBalancerRuleClassName, and the value is the implementation class of IRule.

When tested again, it was found that the results became random:

8.Hystrix

8.1. Introduction

Home page: https://github.com/Netflix/Hystrix/


Hystix is an open source delay and fault tolerance Library of Netflix, which is used to isolate access to remote services and prevent cascading failures.

8.2. Avalanche problem

In microservices, the calling relationship between services is complex. A request may need to call multiple microservice interfaces to realize, which will form a very complex calling link:


As shown in the figure, A business request needs to call four services A, P, H and I, which may call other services. If an exception occurs in A service at this time:


For example, if an exception occurs in microservice I and the request is blocked, the user request will not be responded to, and the thread of tomcat will not be released. Therefore, more and more user requests come and more threads will be blocked:


The number of threads and concurrency supported by the server is limited, and the requests are blocked all the time, which will lead to the depletion of server resources, resulting in the unavailability of all other services, forming an avalanche effect.

This is like an automobile production line that needs different parts to produce different cars. If a part cannot be used for various reasons, the whole car will not be able to assemble and fall into the state of waiting for parts. Assembly can not continue until the parts are in place. At this time, if many models need this part, the whole factory will fall into a waiting state, resulting in the paralysis of all production. The scope of a part is expanding.

The methods used by Hystrix to solve the avalanche problem mainly include:

  • Thread isolation
  • service degradation

8.3. Thread isolation & service degradation

8.3.1 principle

Thread isolation diagram

Interpretation:

  • Hystrix allocates a small thread pool for each dependent service call. If the thread pool is full, the call will be rejected immediately. By default, queuing is not used to speed up the failure determination time.
  • The user's request will no longer directly access the service, but through the idle thread in the thread pool. If the thread pool is full or the request times out, it will be degraded.

Service degradation: core services can be guaranteed first.

When the user's request fails, it will not be blocked, nor will it wait endlessly or see the system crash. At least one execution result can be seen (for example, return a friendly prompt).

Although service degradation will lead to request failure, it will not lead to blocking. At most, it will affect the resources in the thread pool corresponding to the dependent service and will not respond to other services. Conditions that trigger the degradation of the Hystrix service:

  • Thread pool full
  • request timeout

8.3.2. Hands on practice

Service degradation: timely return the result of service call failure, so that the thread will not be blocked by waiting for the service

  1. Introduce dependency

Add the following dependencies in the pom.xml file of the consumer demo consumer system:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. Open fuse

Add a comment on the startup class ConsumerApplication: @ enablercircuitbreaker

@SpringBootApplication 
@EnableDiscoveryClient 
@EnableCircuitBreaker 
public class ConsumerApplication { 
	// ... 
}

We can see that there are more and more annotations on our classes. The above three annotations are often introduced into micro services, so Spring provides a composite annotation: @ SpringCloudApplication

Therefore, we can use this combined annotation instead of the previous three annotations

@SpringCloudApplication 
public class ConsumerApplication { 
	// ... 
}
  1. Write degradation logic

When the call of the target service fails, we hope to fail quickly and give the user a friendly prompt. Therefore, it is necessary to write the degradation processing logic in case of failure in advance, and use hystrix command to complete it.

Modify the ConsumerController.java processor class as follows:

@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/{id}")
	@HystrixCommand(fallbackMethod = "queryByIdFallback")
    public String queryById(@PathVariable Long id) {

        String url = "http://user-service/user/" + id;
        return restTemplate.getForObject(url, String.class);
    }

    public String queryByIdFallback(Long id) {
        log.error("Failed to query user information. id: {}", id);
        return "Sorry, the Internet is too crowded!";
    }
}
  • Pay attention to; Because the degraded logic method must guarantee the same parameter list and return value declaration as the normal logic method.
  • The User object returned in the failure logic does not make much sense. Generally, a friendly prompt will be returned. Therefore, the method of queryById is transformed to return String, which is also Json data anyway. In this way, it is more convenient to return an error description in the failure logic.

explain:

  • @HystrixCommand(fallbackMethod = "queryByIdFallBack"): method test used to declare a degraded logic:

Test:
When the user service provides services normally, the access is the same as before. However, when you shut down the user service, you will find that the page returns the degradation processing information:

  1. Default fallback

The fallback was written on a business method just now. If there are many such methods, wouldn't it be necessary to write a lot. Therefore, the fallback configuration can be added to the class to realize the default fallback; Transform ConsumerController.java again

@RestController
@RequestMapping("/consumer")
@Slf4j
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/{id}")
//    @HystrixCommand(fallbackMethod = "queryByIdFallback")
    @HystrixCommand
    public String queryById(@PathVariable Long id) {
        if (id == 1) {
            throw new RuntimeException("Too busy");
        }

        String url = "http://user-service/user/" + id;
        return restTemplate.getForObject(url, String.class);
    }

    public String queryByIdFallback(Long id) {
        log.error("Failed to query user information. id: {}", id);
        return "Sorry, the Internet is too crowded!";
    }

    public String defaultFallback() {
        return "Default prompt: sorry, the network is too crowded!";
    }
}

@DefaultProperties(defaultFallback = "defaultFallBack"): indicates the unified failure degradation method on the class; The return type of all methods in this class should be consistent with the return type of the failed method.

  1. Timeout setting

In the previous case, the request will return an error message after more than 1 second. This is because the default timeout of Hystrix is 1. We can modify this value through configuration; Modify consumer demo \ SRC \ main \ resources \ application.yml and add the following configuration:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000 #Service degradation timeout

This configuration applies to all global methods. To facilitate copying to the yml configuration file, you can copy
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000 will be automatically formatted in the yml file and then modified.

To trigger timeout, you can sleep for 2 seconds in the method of user service \ SRC \ main \ Java \ com \ LXS \ user \ service \ userservice.java;

@RequestMapping("/{id}") 
public User queryById(@PathVariable("id") Long id) {
	try {
		Thread.sleep(2000);
	}catch (InterruptedException e) { 
		e.printStackTrace();
	}
	return userService.queryById(id);
}

Test:


It can be found that the requested time has reached 2s +, which proves that the configuration is effective. If the modification time is modified to less than 2 seconds, it can be accessed normally.

8.4 service fuse

8.4.1 fusing principle

In service fusing, the fuse used is also called Circuit Breaker, and its English word is: Circuit Breaker fusing mechanism is similar to the circuit fusing principle used at home; In case of short circuit, the circuit can be fused immediately to avoid disaster. After the application service is fused in the distributed system; The service caller can judge which services are slow or have a large number of timeouts, and can actively fuse these services to prevent the whole system from being dragged down.

The service fuse mechanism of Hystrix can realize elastic fault tolerance; When the service request situation improves, it can be reconnected automatically. The subsequent requests are directly rejected by means of circuit breaking. After a period of time (5 seconds by default), some requests are allowed to pass. If the call is successful, the circuit breaker will return to the closed state. Otherwise, it will continue to open and reject the requested service.

Fuse state machine model of Hystrix:


The state machine has three states:

  • Closed: closed state (circuit breaker closed), all requests are accessed normally.
  • Open: open status (circuit breaker open), all requests will be degraded. Hystrix will count the request conditions. When the percentage of failed requests reaches the threshold within a certain time, it will trigger the fuse and the circuit breaker will be fully opened. The default threshold of failure ratio is 50%, and the number of requests is no less than 20.
  • Half Open: Half Open state, not permanent. After the circuit breaker is opened, it will enter sleep time (5S by default). Then the circuit breaker will automatically enter the Half Open state. At this time, some requests will be released to pass. If these requests are healthy, the circuit breaker will be closed. Otherwise, it will remain open and sleep timing will be performed again.

Analysis diagram:

8.4.2 hands on practice

In order to accurately control the success or failure of the request, a section of logic is added to the processor business method of the consumer demo; Modify ConsumerController.java

@GetMapping("{id}") 
@HystrixCommand
public String queryById(@PathVariable Long id) {
	if (id == 1) { 
		throw new RuntimeException("Too busy");
	}
	String url = "http://user-service/user/" + id;
	String user = restTemplate.getForObject(url, String.class);
	return user;
}

In this way, if the parameter id is 1, it will fail and all other cases will succeed( Don't forget to clear the sleep logic in the user service) we prepare two request windows:

When we madly access a request with id 1 (more than 20 times), a fuse will be triggered. The circuit breaker will open and all requests will be degraded.

At this time, when you access the request with id 2, you will find that the returned request also fails, and the failure time is very short, only about 20 milliseconds; Because 2 is OK after entering the half open state.


However, the default fusing trigger has high requirements and short sleep time window. For the convenience of testing, we can modify the fusing strategy through configuration:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000 #Service degradation timeout
      circuitBreaker:
        errorThresholdPercentage: 50 # Trigger fusing error proportional threshold, default 50%
        sleepWindowInMilliseconds: 10000 # Sleep duration after fusing, the default value is 5 seconds
        requestVolumeThreshold: 10 # The minimum number of fuse trigger requests. The default value is 20

To facilitate copying the above configuration, you can use the following format to copy it into the yml file, which will be automatically formatted:

hystrix.command.default.circuitBreaker.requestVolumeThreshold=10
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=10000
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000

The above configuration items can refer to the HystrixCommandProperties class.

9.Feign

In the previous study, we used Ribbon's load balancing function to greatly simplify the code when calling remotely:

String baseUrl = "http://user-service/user/";
User user = this.restTemplate.getForObject(baseUrl + id, User.class)

If you learn this, you may need to write a lot of similar repetitive code in the future. The format is basically the same, but the parameters are different. Is there a more elegant way to optimize this code again?

This is the function of Feign we will learn next.

2.1. Introduction

Why camouflage?

Feign can hide the Rest request and disguise it as a Controller similar to spring MVC. You don't have to splice URLs, splice parameters and so on. Let feign do everything.

Project home page: https://github.com/OpenFeign/feign

2.2. Quick start

2.2.1. Import dependency

<dependency> 
	<groupId>org.springframework.cloud</groupId> 
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.2.2.Feign's client

@FeignClient("user-service") 
public interface UserClient {
	//http://user-service/user/123
	@GetMapping("/user/{id}")
	User queryById(@PathVariable("id") Long id);
}
  • First, this is an interface. Feign will help us generate implementation classes through dynamic proxies. This is very similar to Mybatis's mapper
  • @FeignClient, declare that this is a Feign client, and specify the service name through the value attribute
  • The methods defined in the interface completely adopt the annotations of spring MVC. Feign will help us generate URL s according to the annotations and access the results
  • @/ user in GetMapping, please don't forget; Because Feign needs to splice accessible addresses

Write a new controller class ConsumerFeignController and use UserClient to access:

@RestController
@RequestMapping("/cf")
public class ConsumerFeignController {

    @Autowired
    private UserClient userClient;

    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id) {
        return userClient.queryById(id);
    }
}

2.2.3. Enable Feign function

On the ConsumerApplication startup class, add an annotation to enable the Feign function

@SpringCloudApplication
@EnableFeignClients //Enable feign function
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

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

Ribbon load balancing has been automatically integrated in Feign, so you don't need to define your own RestTemplate for load balancing configuration.

2.2.4. Start up test:

Access interface: http://localhost:8080/cf/2

The results were obtained normally.

2.3. Load balancing

Ribbon dependency and automatic configuration have been integrated in Feign itself:


Therefore, there is no need to introduce additional dependencies and register the RestTemplate object.

The ribbon built in Fegin sets the request timeout length by default, which is 1000 by default. We can modify the timeout length through manual configuration:

ribbon: 
	ReadTimeout: 2000 # Read timeout length
	ConnectTimeout: 1000 # Timeout for establishing link

Or specify for a specific service

user-service
	ribbon: 
		ReadTimeout: 2000 # Read timeout length
		ConnectTimeout: 1000 # Timeout for establishing link

Add sleep time 2s test in user service

Because the ribbon has an internal retry mechanism, once it times out, it will automatically re initiate the request. If you do not want to retry, you can add a configuration:

Modify consumer demo \ SRC \ main \ resources \ application.yml and add the following configuration:

ribbon:
	ConnectTimeout: 1000 # Connection timeout duration
	ReadTimeout: 2000 # Data communication timeout duration
	MaxAutoRetries: 0 # Number of retries for the current server
	MaxAutoRetriesNextServer: 0 # How many service retries
	OkToRetryOnAllOperations: false # Do you want to retry all request methods

2.4.Hystrix support

Feign also integrates with Hystrix by default:

However, it is off by default. It needs to be enabled through the following parameters:

  1. Modify consumer demo \ SRC \ main \ resources \ application.yml and add the following configuration:
feign: 
	hystrix: 
		enabled: true # Turn on the fusing function of Feign

However, the Fallback configuration in Feign is not as simple as that in Ribbon.

  1. First, define a class to implement the UserFeignClient just written as the fallback processing class
@Component
public class UserClientFallback implements UserClient {

    @Override
    public User queryById(Long id) {
        User user = new User();
        user.setName("User exception");
        return user;
    }
}
  1. Then, in UserClient, specify the implementation class just written
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
	@GetMapping("/user/{id}")
	User queryUserById(@PathVariable("id") Long id);
}
  1. Restart test:

Restart, start the consumer demo and close the user service service, and then visit the following page: http://localhost:8080/cf/7

2.5. Request compression (understand)

Spring Cloud Feign supports GZIP compression of requests and responses to reduce performance loss during communication. The compression function of request and response can be enabled through the following parameters:

feign: 
	compression: 
		request: 
			enabled: true # Turn on request compression
		response: 
			enabled: true # Turn on response compression

At the same time, we can also set the requested data type and the lower limit of the size to trigger compression:

feign: 
	compression: 
		request: 
			enabled: true # Turn on request compression
			mime-types: text/html,application/xml,application/json # Set compressed data type
			min-request-size: 2048 # Sets the lower limit of the size that triggers compression

Note: the above data type and the lower limit of compression size are the default values.

2.6. Log level (understand)

As mentioned earlier, set the log level through logging.level.lxs.xx=debug. However, this will not work for the Fegin client. Because a new instance of Fegin.Logger will be created when the client modified by the @ FeignClient annotation is proxy. We need to specify the level of this log.

  1. In the configuration file of consumer demo, set the log level under the com.lxs package to debug. Modify consumer-demo \ SRC \ main \ resources \ application.yml and add the following configuration:
logging: 
	level: 
		com.lxs: debug
  1. Write a configuration class to define the log level
@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
    	//Record the details of all requests and responses, including header information, request body and metadata
        return Logger.Level.FULL;
    }
}

The Level specified here is FULL. Feign supports four levels:

  • NONE: no log information is recorded, which is the default value.
  • BASIC: only record the requested method, URL, response status code and execution time
  • HEADERS: on the basis of BASIC, additional header information of request and response is recorded
  • FULL: records the details of all requests and responses, including header information, request body and metadata.
  1. Specify the configuration class in the @ FeignClient annotation on the UserClient interface class of the consumer Demo:

Don't forget to remove the sleep time of user service

@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration = FeignConfig.class)
public interface UserClient {

    //String url = "http://user-service/user/" + id;
    @GetMapping("/user/{id}")
    public User queryById(@PathVariable Long id);

}
  1. Restart the project to see the log of each access:

10. Spring Cloud Gateway

10.1. Introduction

  • Spring Cloud Gateway is a gateway service developed on the spring official website based on Spring 5.0, Spring Boot 2.0, Project Reactor and other technologies.
  • Spring Cloud Gateway provides the basic functions of the gateway based on the Filter chain: security, monitoring / embedding point, current limiting, etc.
  • Spring Cloud Gateway provides a simple, effective and unified API routing management method for microservice architecture.
  • Spring Cloud Gateway is a solution to replace Netflix Zuul.

The core of the Spring Cloud Gateway component is a series of filters that can forward (route) requests sent by clients to corresponding microservices. Spring Cloud Gateway is a firewall and agent added at the forefront of the whole microservice to hide the IP port information of microservice nodes, so as to strengthen security protection. Spring Cloud Gateway itself is also a micro service, which needs to be registered with Eureka service registry.

The core functions of the gateway are: filtering and routing

10.2 architecture after gateway is added

Whether it is a request from the client (PC or mobile terminal) or an internal call of the service. All requests for services can pass through the Gateway, and then the Gateway can realize authentication, dynamic routing and so on. The Gateway is the unified entrance to our services.

10.3. Core concepts

  • Route: consists of an ID, a destination URL, a set of assertion factories, and a set of filters. If the route assertion is true, the request URL matches the configured route.
  • Predicate the input type of the assertion function in the Spring Cloud Gateway is ServerWebExchange in the Spring 5.0 framework. The assertion function of Spring Cloud Gateway allows developers to define and match any information from HTTP Request, such as request header and parameters.
  • Filter a standard Spring WebFilter. There are two types of filters in Spring Cloud Gateway: Gateway Filter and Global Filter. The filter will modify the request and response.

10.4. Quick start

Requirement: route the request containing / user to via the LXS gateway system http://127.0.0.1:9091/user/ User id

10.4.1. New construction



Open the LXS springcloud \ LXS gateway \ pom.xml file and modify it as follows:

<?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">
    <parent>
        <artifactId>lxs-springcloud</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lxs-gateway</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>

10.4.2. Write startup class

Create the com.lxs.gateway.GatewayApplication startup class in LXS gateway

@SpringBootApplication
@EnableDiscoveryClient //Enable eureka registration discovery function
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

10.4.2. Write configuration

Create the LXS gateway \ SRC \ main \ resources \ application.yml file as follows:

server:
  port: 10010
spring:
  application:
    name: api-gateway
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

You need to use the gateway to proxy the user service. First look at the service status in the control panel:

  • ip: 127.0.0.1
  • Port: 9091

Modify the LXS gateway \ SRC \ main \ resources \ application.yml file to:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

All requests that conform to the Path rule are proxied to the address specified by the uri parameter. In this example, we will proxy requests starting with / user / * * in the Path to http://127.0.0.1:9091

10.4.5. Start up test

The mapping path of the configuration rule needs to be added to the access path. We access: http://localhost:10010/user/7

http://localhost:10010/user/7 -> http://localhost:9091/user/7

10.5. Service oriented routing

In the routing rule just now, the service address corresponding to the path is written dead! This is obviously unreasonable if there are multiple instances of the same service. According to the name of the service, go to Eureka registry to find the list of all instances corresponding to the service, and then conduct dynamic routing!

10.5.1. Modify the mapping configuration and obtain it through the service name

Modify the LXS gateway \ SRC \ main \ resources \ application.yml file as follows:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

When the protocol used in the uri in the routing configuration is lb (take uri: LB: / / user service as an example), the gateway will use the LoadBalancerClient to parse the user service into the actual host and port through eureka, and carry out ribbon load balancing.

10.5.2. Start up test

Start LXS gateway again. This time, when the gateway acts as an agent, it will use the Ribbon for load balancing access: http://localhost:10010/user/8 You can see from the log that the load balancer is used:

10.6. Routing prefix

If the request address of the client is inconsistent with the service address of the microservice, you can add and remove the path prefix by configuring the path filter.

Address of service provider: http://127.0.0.1:9091/user/8

10.6.1. Add prefix

In the gateway, you can add the address in the mapping path by configuring the filter PrefixPath of the route;

Modify the LXS gateway \ SRC \ main \ resources \ application.yml file:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
          filters:
            # Add prefix to request path
            - PrefixPath=/user
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true


PrefixPath=/xxx specifies the prefix to be added to the route. That is:

10.6.2. Remove prefix

In the gateway, the address in the mapping path can be removed by configuring the route filter StripPrefix;

Modify the LXS gateway \ SRC \ main \ resources \ application.yml file:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
          filters:
            # 1: Remove one path, 2: remove 2 paths, and so on
            - StripPrefix=1
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

The number of prefixes to be removed from the route is specified by StripPrefix=1. For example, the path / api/user/1 will be proxied to / user/1. That is:

and so on

10.7. Filter

10.7.1. Introduction

One of the important functions of Gateway is to realize the authentication of request. This action is often implemented through the filter provided by the Gateway. The functions in the previous routing prefix chapter are also implemented using filters.

The Gateway has dozens of built-in filters, and the common built-in filters are:

Filter nameexplain
AddRequestHeaderAdd a Header to the matching request
AddRequestParametersAdd parameters to the request route on the match
AddResponseHeaderAdd a Header for the response returned from the gateway
StripPrefixRemove the prefix from the request path on the match


Detailed description reference Official website link

Configure global default filters

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
          filters:
            # 1: Remove one path, 2: remove 2 paths, and so on
            - StripPrefix=1
      default-filters:
        - AddResponseHeader=X-Response-Foo, Bar
        - AddResponseHeader=myname, lxs
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

After the above configuration, access http://localhost:10010/api/user/7 If necessary; The following information can be viewed from its response:

Filter type: there are two types of filters in Gateway implementation;

  • Local filter: configured through spring.cloud.gateway.routes.filters under specific routes and only works on the current route;
    If spring.cloud.gateway.default-filters is configured, it will take effect for all routes, which is also regarded as a global filter; However, the implementation of these filters is to implement the GatewayFilterFactory interface.
  • Global filter: it does not need to be configured in the configuration file and acts on all routes; Just implement the GlobalFilter interface.

10.7.2. Execution life cycle

The Filter life cycle of Spring Cloud Gateway is similar to that of Spring MVC. There are two interceptors: "pre" and "post"“ Pre "and" post "will be called after the request is executed and executed.

The pre and post here can be implemented before and after the filter method is executed by the GatewayFilterChain of the filter

10.7.3. Usage scenario

Common application scenarios are as follows:

  • Request authentication: generally, before the GatewayFilterChain executes the filter method, if it is found that there is no access permission, it will directly return null.
  • Exception handling: generally, after the GatewayFilterChain executes the filter method, the exception is recorded and returned.
  • Statistics of service call duration: the gateway filterchain performs statistics according to the time before and after the filter method is executed.

10.8. Custom filter

10.8.1. Custom local filter

Requirements:

In the filter (MyParamGatewayFilterFactory) http://localhost:10010/api/user/8?name=lxs The value of the parameter name in is obtained and output to the console; And the parameter name is variable, that is, it may not always be name; You can configure the parameter name when configuring the filter.

Configure a filter for a route in application.yml. The filter can specify the value of the request parameter with the name in the console output configuration file.

  1. Write filter
@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {

    public MyParamGatewayFilterFactory() {
        super(Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            //http://localhost:10010/api/user/8?name=lxs config.param = name
            ServerHttpRequest request = exchange.getRequest();
            if (request.getQueryParams().containsKey(config.param)) {
                request.getQueryParams().get(config.param).forEach((v) -> {
                    System.out.printf("--Local filter--Get parameters %s = %s ----", config.param, v);
                });
            }

            return chain.filter(exchange); //Execute request
        };
    }


    //Read parameters of filter configuration
    public static class Config {
        //Corresponds to the filter parameters configured in the application.yml configuration file
        private String param;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }
    }

}
  1. Modify profile
server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
          filters:
            # 1: Remove one path, 2: remove 2 paths, and so on
            - StripPrefix=1
            - MyParam=name
      default-filters:
        - AddResponseHeader=X-Response-Foo, Bar
        - AddResponseHeader=myname, lxs
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

Note: the name of the custom filter should be * * * GatewayFilterFactory

Test access: http://localhost:10010/api/user/7?name=lxs Check whether the background outputs name and lxs; But if you visit http://localhost:10010/api/user/7?name2=kaikeba Will not be output

10.8.2. User defined global filter

Requirement: write a global filter and check whether the request carries the token request header in the filter. If the token request header exists, it will be released; If the token is empty or does not exist, set the returned status code as: unauthorized and no longer executed.

Write the global filter class MyGlobalFilter in the LXS Gateway project

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("Global filter");
        String token = exchange.getRequest().getHeaders().getFirst("token");
        if (StringUtils.isBlank(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        //The smaller the value, about the first execution
        return 1;
    }
}

visit http://localhost:10010/api/user/7

visit http://localhost:10010/api/user/7?token=abc

10.9. Load balancing and fusing (understand)

By default, Ribbon load balancing and Hystrix fuse mechanism have been integrated in the Gateway. However, all timeout policies are the default values. For example, the fuse timeout is only 1S, which is easy to trigger. Therefore, it is recommended to configure manually:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 #Service degradation timeout, 1S by default
ribbon:
  ConnectTimeout: 1000 # Connection timeout duration
  ReadTimeout: 2000 # Data communication timeout duration
  MaxAutoRetries: 0 # Number of retries for the current server
  MaxAutoRetriesNextServer: 0 # How many service retries

10.10. Gateway cross domain configuration

Generally, the gateway is the unified entrance of all micro services, and cross domain problems will inevitably occur when called.

Cross domain: in js request access, if the access address is inconsistent with the domain name, ip or port number of the current server, it is called cross domain request. If it is not solved, the return result of the corresponding address cannot be obtained.

For example, from http://localhost:9090 js access in http://localhost:9000 Because the ports are different, the data is also a cross domain request.

If there is a cross domain problem when accessing the Spring Cloud Gateway server; You can configure the gateway server to determine which services can be requested across domains; The specific configuration is as follows:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        #The id of the route can be arbitrary
        - id: user-service-rote
          #Proxy's microservice address
          #uri: http://127.0.0.1:9091
          uri: lb://user-service
          #Route assertion: mapping paths can be configured
          predicates:
            - Path=/api/user/**
          filters:
            # 1: Remove one path, 2: remove 2 paths, and so on
            - StripPrefix=1
            - MyParam=name
      default-filters:
        - AddResponseHeader=X-Response-Foo, Bar
        - AddResponseHeader=myname, lxs
      globalcors:
        corsConfigurations:
          '[/**]':
            #allowedOrigins: * # This way of writing or the following can be used, * means all
            allowedOrigins:
              - "http://docs.spring.io"
            allowedMethods:
              - GET
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 #Service degradation timeout, 1S by default
ribbon:
  ConnectTimeout: 1000 # Connection timeout duration
  ReadTimeout: 2000 # Data communication timeout duration
  MaxAutoRetries: 0 # Number of retries for the current server
  MaxAutoRetriesNextServer: 0 # How many service retries

explain

The above configuration indicates that it is allowed to http://docs.spring.io Get service data by get request.

allowedOrigins specifies the server address allowed to access, such as: http://localhost:10000 It's OK.

'[/ * *]' indicates the address of all requests to access the gateway server

Specific instructions on the official website: https://cloud.spring.io/spring-cloud-static/spring-cloud- gateway/2.1.1.RELEASE/multi/multi__cors_configuration.html

10.11. High availability of gateway (understand)

Start multiple Gateway services and automatically register with Eureka to form a cluster. In case of internal service access, access the Gateway and automatic load balancing. No problem.

However, the Gateway is more external access, such as PC terminal, mobile terminal, etc. They can't load balance through Eureka, so what should I do? At this time, other service gateways can be used to proxy the Gateway. For example: Nginx

10.12. Difference between gateway and Feign

  • As the traffic entrance of the whole application, the Gateway receives all requests, such as PC and mobile terminal, and forwards different requests to different processing micro service modules, which can be regarded as nginx; In most cases, it is used for permission authentication and server-side flow control
  • Feign exposes some service interfaces of the current microservices and is mainly used for service calls between microservices

11.Spring Cloud Config distributed configuration center

3.1. Introduction

In the distributed system, due to the large number of services, the configuration files are scattered in different micro service projects, which is inconvenient to manage. In order to facilitate centralized management of configuration files, distributed configuration center components are required. In Spring Cloud, Spring Cloud Config is provided. It supports placing configuration files locally in the configuration service and in remote Git warehouses (GitHub and code cloud).

The architecture after using the Spring Cloud Config configuration center is shown in the figure below

The configuration center is also a micro service in essence. You also need to register with Eureka service registry!

3.2. Git configuration management

Well known Git remote warehouses include foreign GitHub and domestic gitee; However, when using GitHub, domestic users often encounter the problem that the access speed is too slow, and sometimes they can't connect. If you want to experience better, you can use the domestic Git hosting service - Code cloud (gitee.com). Compared with GitHub, codecloud also provides a free Git warehouse. In addition, it also integrates code quality detection, project demonstration and other functions. For team collaborative development, code cloud also provides project management, code hosting and document management services. The remote Git warehouse used in this chapter is code cloud.

Code cloud access address: https://gitee.com/

3.2.2. Create remote warehouse

First, to use the private remote git warehouse on the code cloud, you need to register an account first; Please visit the website and register your account, then use the account login code to the cloud console and create a public warehouse.

3.2.3. Create profile

Create a configuration file that needs to be uniformly configured and managed in the new warehouse.
Naming method of configuration file: {application}-{profile}.yml or {application}-{profile}.properties

  • Application is the name of the application
  • profile is used to distinguish development environment, test environment, production environment, etc

For example, user-dev.yml represents the configuration file used in the user microservice development environment.

Here, copy the contents of the configuration file application.yml of the user service project as the contents of the user-dev.yml file. The specific configuration is as follows:


After creating the user-dev.yml configuration file, the repository in gitee is as follows:

3.3. Build micro service of configuration center

3.3.1. Create project

To create a configuration center microservice project:

Add dependency and modify config server \ pom.xml as follows:

<?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">
    <parent>
        <artifactId>lxs-springcloud</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>config-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        
    </dependencies>

</project>

3.3.2. Startup

Create the startup class of config server of the configuration center project;

Config server \ SRC \ main \ Java \ com \ LXS \ config \ configserverapplication.java is as follows:

@SpringBootApplication
@EnableConfigServer //Open configuration service
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}

3.3.3. Configuration file

server:
  port: 12000
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/lxsong77/my-config.git
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Note that the above spring.cloud.config.server.git.uri is the warehouse address created in the code cloud; You can modify the warehouse address created for yourself

3.3.4. Start up test

Start the eureka registry and configuration center; Then visit http://localhost:12000/user-dev.yml , check whether the user-dev.yml file managed in code cloud storage can be output. In addition, you can modify user-dev.yml on gitee and refresh the above test address, and you can also get the latest data in time.

3.4. Get configuration center configuration

The microservice of the configuration center has been built. Next, we need to transform the user microservice user service. The configuration file information is no longer provided by the microservice project, but obtained from the configuration center. The user service project is reconstructed as follows.

3.4.1. Add dependency

Add the following dependencies to the pom.xml file in the user service project:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

3.4.2. Modify configuration

  1. Delete the user service \ SRC \ main \ resources \ application.yml file of the user service project (because it is obtained from the configuration center)
  2. Create user service project user service \ SRC \ main \ resources \ bootstrap.yml configuration file
spring:
  cloud:
    config:
      #The application of configuration file in git warehouse is consistent
      name: user
      #The configuration files in git warehouse are consistent with each other
      profile: dev
      #git warehouse branch is consistent
      label: master
      discovery:
        # Use configuration center
        enabled: true
        # Configuration center service name
        service-id: config-server
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Modified structure of user service project:

  • The bootstrap.yml file is also the default configuration file for Spring Boot, and its loading time is earlier than that of application.yml.
  • Although application.yml and bootstrap.yml are the default configuration files of Spring Boot, their positioning is different. Bootstrap.yml can be understood as some system level parameter configurations, which are generally unchanged. Application.yml can be used to define application level parameters. If used in conjunction with spring cloud config, the files defined in application.yml can be dynamically replaced.
  • To sum up, the bootstrap.yml file is equivalent to the boot file when the project is started, and the content is relatively fixed. application.yml file is some general configuration parameters of microservices, which change frequently.

3.4.3. Start up test

Start the registration center Eureka server, the configuration center config server, and the user service user service. If no error is reported, the configuration center content has already been used. You can go to the registration center and check the user service.

12.Spring Cloud Bus service bus

12.1. Problems

Previously, it has been completed to store the configuration files in the micro service in the remote git warehouse, and pull the configuration files from the GIT warehouse through the micro service in the configuration center. When the user micro service is started, it will connect to the configuration center to obtain the configuration information, so as to start the user micro service. If we update the configuration file in Git warehouse, can the user micro service receive the new configuration information and update it in time

12.1.1. Modify remote Git configuration

Modify the user-dev.yml file on the code cloud and add an attribute test.name.

12.1.2. Modifying UserController

Modify the processor class in the user service project; User service \ SRC \ main \ Java \ com \ LXS \ user \ controller \ usercontroller.java is as follows:

@RestController
@RequestMapping("/user")
@RefreshScope //Profile change auto refresh attribute
public class UserController {

    @Autowired
    private UserService userService;

    @Value("${test.name}")
    private String name;

    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id) {
        System.out.println("Configuration center configured test.name = " + name);
        return userService.queryById(id);
    }
	@RequestMapping("/user/all")
	public String all() { 
		return "return all user list..........";
	}
}

12.1.3. Test

Start the registration center Eureka server, configuration center config server and user service in turn; Then modify the configuration information in the Git warehouse, access the user's Micro service and view the output content.

Conclusion: by viewing the output results of the user microservice console, we can find that the modification of the configuration file in Git warehouse has not been updated to the user microservice in time. It can take effect only by restarting the user microservice.

How to update the configuration without restarting the microservice? Spring Cloud Bus can be used to automatically update the configuration.

It should be noted that the underlying layer of Spring Cloud Bus is implemented based on RabbitMQ. By default, the local message queue service is used, so the local RabbitMQ service needs to be started in advance (only after RabbitMQ is installed), as follows:

12.2. Introduction to spring cloud bus

Spring Cloud Bus connects distributed nodes with lightweight message broker, which can be used to change broadcast configuration files or monitor and manage services. That is, the message bus can monitor microservices and realize the communication between applications. The optional message brokers for Spring Cloud Bus are RabbitMQ and Kafka.

After using Bus:

preparation

In the installation data \ otp_win64_23.0.exe and rabbitmq-server-3.8.5.exe

12.3. Transformation of configuration center

Add Spring Cloud Bus related dependencies in the pom.xml file of the config server project

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

In the config server project, modify the application.yml file as follows:

server:
  port: 12000
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/lxsong77/my-config.git
  # Configure rabbitmq information; If both are consistent with the default values, no configuration is required
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
management:
  endpoints:
    web:
      exposure:
        # Expose the address of the trigger message bus
        include: bus-refresh

12.4. Transformation of user services

  1. Add Spring Cloud Bus related dependencies in pom.xml of user microservice user service project
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. Modify the bootstrap.yml of the user service project as follows
spring:
  cloud:
    config:
      #The application of configuration file in git warehouse is consistent
      name: user
      #The configuration files in git warehouse are consistent with each other
      profile: dev
      #git warehouse branch is consistent
      label: master
      discovery:
        # Use configuration center
        enabled: true
        # Configuration center service name
        service-id: config-server
  # Configure rabbitmq information; If both are consistent with the default values, no configuration is required
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  1. User controller of user micro service user service project

12.5. Testing

The transformation of the configuration center microservice and user microservice has been completed. Let's test whether the user microservice can automatically update the configuration information without restarting when we modify the configuration file in Git warehouse. Test steps:

  • Step 1: start the registration center Eureka server, the configuration center config server, and the user service user service in turn
  • Step 2: access user microservices http://localhost:9091/user/7 ; View IDEA console output
  • Step 3: modify the test.name content of the configuration file user-dev.yml in the Git repository
  • Step 4: use the Postman or RESTClient tool to send a POST request to access the address http://127.0.0.1:12000/actuator/bus-refresh
  • Step 5: visit the user micro service system console to view the output results

explain:

  1. Postman or RESTClient is a tool that can simulate a browser to send various requests (POST, GET, PUT, DELETE, etc.).
  2. Request address http://127.0.0.1:12000/actuator/bus -The / actor in refresh is fixed.
  3. request http://127.0.0.1:12000/actuator/bus -The refresh address is used to access the message bus service of the configuration center. After receiving the request, the message bus service will send a message to the message queue, and each micro service will listen to the message queue. When the micro service receives the message in the queue, it will re obtain the latest configuration information from the configuration center.

12.6. Complete architecture diagram of spring cloud

Keywords: Java Spring RESTful

Added by johnnyv on Wed, 08 Sep 2021 08:36:52 +0300