Explanation of spring cloud gateway basic operation in 2021

Code address of this series: github.com/JoJoTec/spr...

preface

Next, we will enter another big module on our upgrade path, namely the gateway module. In the gateway module, we abandoned zuul that has entered the maintenance state and selected Spring Cloud Gateway as the internal gateway. The reasons for choosing Spring Cloud Gateway instead of nginx and Kong are:

  1. It is important that the project team is more familiar with Java and asynchronous programming of Project Reactor
  2. We need to use the request based stateful retry pressure sensitive load balancer we implemented earlier in the gateway
  3. Retry needs to be implemented in the gateway
  4. You need to implement instance path breaking in the gateway
  5. Unified business encryption and decryption is required in the gateway
  6. It needs to be implemented in the gateway   BFF (Backends For Frontends) interface, that is, according to the client request, the requests of several different interfaces are combined and returned at one time
  7. Redis should be used in the gateway to record some Token related values

Therefore, we use the Spring Cloud Gateway as the internal gateway. Next, we will implement these functions in turn. At the same time, during the use of this upgrade, the Spring Cloud Gateway also has some pits, such as:

  1. When spring cloud sleuth is used in combination, there will be link information tracking, but link information will be lost in some cases.
  2. The asynchronous API encapsulated by the three-party Reactor (for example, spring data Redis used by Redis) is not well understood, resulting in the occupation of key threads.

But first, we need to briefly understand what components Spring Cloud Gateway includes and what the whole calling process looks like. Since Spring Cloud Gateway is implemented based on spring boot and spring Webflux, we will start with the outer WebFilter, and then analyze how to go to the packaging logic of Spring Cloud Gateway, as well as the components contained in Spring Cloud Gateway, how to forward the request, and what processing has been done after coming back. We will analyze these one by one.

Create a simple API gateway

In order to analyze the process in detail, let's first create a simple gateway for quick start and analysis.

Create dependencies first:

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>spring-cloud-parent</artifactId>
		<groupId>com.github.jojotech</groupId>
		<version>2020.0.3-SNAPSHOT</version>
	</parent>
	<modelVersion>4.0.0</modelVersion>

	<artifactId>spring-cloud-api-gateway</artifactId>

	<dependencies>
		<dependency>
			<groupId>com.github.jojotech</groupId>
			<artifactId>spring-cloud-webflux</artifactId>
			<version>${project.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>
	</dependencies>
</project>
Copy code

The parent points to the spring cloud parent of our project, adds the spring cloud weblux dependency implemented in the previous section, and also needs to add the spring cloud starter gateway. Since our spring cloud parent has specified the version dependency management of spring cloud parent, it is not necessary to specify the version of spring cloud starter gateway here

Then we start writing the configuration file:

application.yml

server:
  ##Port 8181
  port: 8181
spring:
  application:
    # The microservice name is apiGateway
    name: apiGateway
  cloud:
    gateway:
      httpclient:
        # The timeout of the HTTP connection forwarded by the gateway to other microservices is 500ms
        connect-timeout: 500
        # The timeout of HTTP response forwarded by the gateway to other microservices is 500ms
        response-timeout: 60000
      routes:
        # Write forwarding rules
        - id: first_route
          # Forward to microservice test service
          uri: lb://test-service
          # Which paths are included
          predicates:
            - Path=/test-ss/**
          #  For the micro service access path forwarded to, remove the first block in the path, that is, remove / test SS
          filters:
            - StripPrefix=1
    loadbalancer:
      # Specify a zone because we added the logic that only instances of the same zone can access each other in load balancing
      zone: test
      ribbon:
        # Close ribbon
        enabled: false
      cache:
        # Cache time of local microservice instance list
        ttl: 5
        # The cache size, which is set to 256 by default, depends on how many other microservices your microservice calls
        capacity: 256
    discovery:
      client:
        simple:
          # Using the simple discovery client service in spring common to discover the client is to write the microservice instance in the configuration file
          instances:
            # Specify the instance list of the micro service test service
            test-service:
              - host: httpbin.org
                port: 80
                metadata:
                  # Specify the zone of this instance because we added the logic that only instances of the same zone can access each other in load balancing 
                  zone: test
eureka:
  client:
    # Turn off eureka
    enabled: false
 Copy code

Finally, write the startup entry class:

package com.github.jojotech.spring.cloud.apigateway;

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

@SpringBootApplication(scanBasePackages = "com.github.jojotech.spring.cloud.apigateway")
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

Copy code

Startup, access path:   http://127.0.0.1:8181/test-ss/anything, you can see that the request is sent to the anything path of httpbin.org, and this interface will return all the information in the request.

In this way, we have implemented a simple gateway. Next, let's analyze its workflow and source code in detail.

The core of request processing in asynchronous environment - spring boot + WebHandler of spring Webflux

The external service container of the Simple Gateway we created is actually a container based on Netty and Project Reactor. We skip these and directly enter the processing logic related to Spring Boot. We only need to know that the request and its corresponding response will be encapsulated into an outer container   ServerHttpRequest request   and   Serverhttpresponse   org.springframework.http.server.reactive   This bag is under).

Then, it will be handed over to   WebHandler   Process. WebHandler   The implementation of is actually a responsibility chain decoration mode, as shown in the figure below. On each floor   WebHandler   Will   request   and   response   Decorate according to your own responsibility, and then hand it over to the inner layer   WebHandler   handle.

HttpWebHandlerAdapter - encapsulates the request as ServerWebExchange

WebHandler   The interface definition is:

public interface WebHandler {
	Mono<Void> handle(ServerWebExchange exchange);
}
Copy code

But the parameters passed in from the outermost layer are   request   and   response, they need to be encapsulated into   Server web exchange, this work is done in the HttpWebHandlerAdapter. In fact, the main task of HttpWebHandlerAdapter is to encapsulate various parameters into   Server web exchange (except for those related to this request)   request   and   response, session manager, session manager, codec configuration, internationalization configuration, and ApplicationContext for extension).

In addition to these, deal with   Forwarded   also   X-Forwarded*   The related Header configuration logic is also carried out here. Then the encapsulated   ServerWebExchange   To the inside   WebHandler   Namely   ExceptionHandlingWebHandler   Continue processing. At the same time, it can be seen from the source code that Mono, which is handed over to the inner layer for processing, also adds the logic of exception handling and recording response information:

HttpWebHandlerAdapter.java

//Leave it to the inner layer to handle the encapsulated ` ServerWebExchange`
return getDelegate().handle(exchange)
        //Record the response log, trace level, which is generally not used
		.doOnSuccess(aVoid -> logResponse(exchange))
		//Handling exceptions not handled by the inner layer generally does not come here
		.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
		//After all processing is completed, set the response to complete
		.then(Mono.defer(response::setComplete));
Copy code

The rest of the inner layer   WebHandler, which we will continue to analyze in the next section

Keywords: Java Spring Back-end

Added by gioahmad on Fri, 26 Nov 2021 04:02:14 +0200