This is the 24th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
This series code address: github.com/JoJoTec/spr…
Next, we move on to another big module on our upgrade path, the gateway module. Gateway Module We abandoned Zuul, which was already in maintenance, and chose Spring Cloud Gateway as the internal Gateway. The reasons for choosing Spring Cloud Gateway over Nginx and Kong are:
- It is important that the Project team is more familiar with Java and asynchronous programming for the Project Reactor
- We need to use the request-based stateful retry pressure-sensitive load balancer we implemented earlier in the gateway
- Retry needs to be implemented in the gateway
- Instance path disconnection needs to be implemented in the gateway
- Unified service encryption and decryption must be performed on the gateway
- Backends For Fronends (BFF) interfaces need to be implemented in the gateway, which returns requests from several different interfaces at a time based on client requests
- You need to log some token-related values in the gateway using Redis
Therefore, we used the Spring Cloud Gateway as the internal Gateway, and we will implement these functions in turn. At the same time, Spring Cloud Gateway also has some pits during this upgrade, such as:
- When spring-cloud-SLEUTH is used, link information can be traced, but link information can be lost in some cases.
- A poor understanding of the asynchronous apis encapsulated by the three-party Reactor (such as spring-data-redis for the aforementioned Redis operation) resulted in critical threads being occupied.
But first, we need to take a quick look at what exactly the Spring Cloud Gateway includes and what the entire invocation process looks like. Since Spring Cloud Gateway is based on Spring-Boot and Spring-WebFlux implementation, we will start with the explanation of the outer WebFilter, and then analyze how to go to the packaging logic of Spring Cloud Gateway. We’ll look at what components the Spring Cloud Gateway contains, how requests are forwarded, and how they are processed when they come back.
Create a simple API gateway
To analyze the process in detail, let’s start by creating a simple gateway that can be quickly picked up and analyzed.
Create the dependency first:
pom.xml
<? The 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" > < the parent > < artifactId > spring - cloud - parent < / artifactId > < groupId > com. Making. Jojotech < / groupId > < version > 2020.0.3 - the SNAPSHOT < / version > < / parent > < modelVersion > 4.0.0 < / modelVersion > < artifactId > spring - the 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 the code
Parent refers to the spring-cloud-parent of our project, and it also adds the spring-cloud-webflux dependency implemented in the previous section, and also needs to add the spring-cloud-starter-gateway. Since the spring-cloud-parent version dependency management has been specified in our spring-cloud-parent, there is no need to specify the spring-cloud-starter-gateway version
Then, we start writing configuration files:
application.yml
ApiGateway name: apiGateway Cloud: gateway: httpClient: Timeout: 500ms response-timeout: 60000 routes: Test -service uri: lb://test-service /test-ss filters: -stripprefix =1 loadbalancer: /test-ss filters: -stripprefix =1 loadbalancer: Zone: test ribbon: # disable ribbon enabled: false cache: # list of local micro service instance cache time TTL: 5 # cache size, you how many other micro micro service call service, how much size is set to the default 256 capacity: 256 discovery: client: simple: Test-service (DiscoveryClient, spring-common, DiscoveryClient, spring-Common) -host: httpbin.org port: 80 Metadata: # Specify the zone of this instance because only instances of the same zone can access each other in load balancing. # Disable Eureka Enabled: falseCopy the 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 the code
Start, access path: http://127.0.0.1:8181/test-ss/anything, you can see the request is sent to the httpbin.org anything path, this interface will return all information in the request.
Thus, 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 + Spring WebFlux WebHandler
We created a simple gateway with an outer layer of service containers based on Netty and Project Reactor, so we skipped that and went straight to Spring Boot-related processing logic. All we need to know is that the request and its corresponding response, Is the outer encapsulation of the container be ServerHttpRequest request and ServerHttpResponse response (in org. Springframework. HTTP. Server. Reactive under this package).
It is then passed to WebHandler for processing. The implementation of WebHandler is actually a chain of responsibility decorator pattern, as shown in the figure below. Each layer’s WebHandler decorates the request and response for its own responsibility and then hands them off to the inner layer’s WebHandler.
HttpWebHandlerAdapter – Encapsulates the request as ServerWebExchange
The interface definition for WebHandler is:
public interface WebHandler {
Mono<Void> handle(ServerWebExchange exchange);
}
Copy the code
But the outermost parameters passed in are Request and Response, and you need to wrap them as ServerWebExchange, which is done inside the HttpWebHandlerAdapter. HttpWebHandlerAdapter encapsulates various parameters as ServerWebExchange (in addition to the request and response associated with this request, there are SessionManager SessionManager, codec configuration, Internationalization configuration also has ApplicationContext for extension.
In addition to this, the configuration logic for forwarding to Forwardedand the related headers of X-Forwarded* also works here. Then encapsulated ServerWebExchange to inner WebHandler ExceptionHandlingWebHandler continue processing. At the same time, from the source can be seen, to the inner processing of Mono also added exception handling and recording response information logic:
HttpWebHandlerAdapter.java
ServerWebExchange 'return getDelegate().handle(exchange) DoOnSuccess (aVoid -> logResponse(exchange)) OnErrorResume (ex -> handleUnresolvedError(Exchange, ex)) Set response to complete. Then (mono.defer (response::setComplete));Copy the code
The remaining inner WebHandlers will be examined in the next section
Wechat search “my programming meow” public account, a daily brush, easy to improve skills, won a variety of offers: