Spring Cloud Gateway series [4] initialization loading process source code analysis

Core source code

Route class

Route is one of the most basic components in gateway, which represents a specific routing information carrier. The routing information consists of an ID, a target URl, a set of assertions, and a set of filters. If the asserted route is true, the requested URI and configuration match.

public class Route implements Ordered {
    private final String id;
    private final URI uri;
    private final int order;
    private final AsyncPredicate<ServerWebExchange> predicate;
    private final List<GatewayFilter> gatewayFilters;
    private final Map<String, Object> metadata;
}

The member properties of Route are described as follows:

attributeeffect
idIdentifier, different from other routes
uriThe destination uri that the route points to, that is, the destination where the client request is finally forwarded
orderIt is used for sorting among multiple routes. The smaller the value, the higher the sorting priority and the higher the matching priority
predicatePredicate, which indicates the precondition matching the Route, that is, it will be routed to the destination uri only if the corresponding conditions are met
gatewayFiltersFilters are used to process aspect logic, such as modifying request headers before routing forwarding
metadataMetadata that describes the route

Asyncpredict interface

AsyncPredicate is a member property of Routed, which is used for condition matching.

It implements the functional interface function < T, R >, function < T, R > provided by Java 8, which is used to obtain another type of data according to one type of data. The former is called pre condition and the latter is called post condition.

public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
    default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
        return new AsyncPredicate.AndAsyncPredicate(this, other);
    }

    default AsyncPredicate<T> negate() {
        return new AsyncPredicate.NegateAsyncPredicate(this);
    }

    default AsyncPredicate<T> not(AsyncPredicate<? super T> other) {
        return new AsyncPredicate.NegateAsyncPredicate(other);
    }

    default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
        return new AsyncPredicate.OrAsyncPredicate(this, other);
    }
}

AsyncPredicate defines three logical operation methods:

methodexplain
andAnd operations, that is, two predicates form one, which needs to be met at the same time.
negateReverse operation, that is, reverse the Predicate matching result.
orOr operation, that is, two predicates form one, and only one of them needs to be met.

ServerWebExchange interface

Note to ServerWebExchange: ServerWebExchange is a contract for HTTP request response interaction. Provides access to HTTP requests and responses, and exposes additional server-side processing related properties and features, such as request properties.

In fact, ServerWebExchange is named service network switch, which stores important request response properties, request instances and response instances, which is a bit like the role of Context.

public interface ServerWebExchange {

    // The KEY of the log prefix attribute is org.springframework.web.server.ServerWebExchange.LOG_ID
    // It can be understood as attributes.set("org.springframework.web.server.ServerWebExchange.LOG_ID", "specific value of log prefix");
    // The function is to splice the prefix value of the KEY when printing the log. The default value is ""
    String LOG_ID_ATTRIBUTE = ServerWebExchange.class.getName() + ".LOG_ID";
    String getLogPrefix();

    // Get ServerHttpRequest object
    ServerHttpRequest getRequest();

    // Get ServerHttpResponse object
    ServerHttpResponse getResponse();
    
    // Returns the request attribute of the current exchange, and the returned result is a variable Map
    Map<String, Object> getAttributes();
    
    // Get request properties according to KEY
    @Nullable
    default <T> T getAttribute(String name) {
        return (T) getAttributes().get(name);
    }
    
    // Get the request attribute according to the KEY and make a non empty judgment
    @SuppressWarnings("unchecked")
    default <T> T getRequiredAttribute(String name) {
        T value = getAttribute(name);
        Assert.notNull(value, () -> "Required attribute '" + name + "' is missing");
        return value;
    }

     // Get the request attribute according to the KEY, and you need to provide a default value
    @SuppressWarnings("unchecked")
    default <T> T getAttributeOrDefault(String name, T defaultValue) {
        return (T) getAttributes().getOrDefault(name, defaultValue);
    } 

    // Returns the currently requested network session
    Mono<WebSession> getSession();

    // Returns the currently requested authenticated user, if any
    <T extends Principal> Mono<T> getPrincipal();  
    
    // Return the requested form data or an empty Map. This method will return a non empty Map only when the content type is application/x-www-form-urlencoded -- this is usually used for form data submission
    Mono<MultiValueMap<String, String>> getFormData();   
    
    // Return the part data or an empty Map requested by multipart. This method will return a non empty Map only when the content type is multipart / form data -- this is usually used for file upload
    Mono<MultiValueMap<String, Part>> getMultipartData();
    
    // Returns the context of Spring
    @Nullable
    ApplicationContext getApplicationContext();   

    // These methods are related to the lastModified attribute
    boolean isNotModified();
    boolean checkNotModified(Instant lastModified);
    boolean checkNotModified(String etag);
    boolean checkNotModified(@Nullable String etag, Instant lastModified);
    
    // URL conversion
    String transformUrl(String url);    
   
    // URL translation mapping
    void addUrlTransformer(Function<String, String> transformer); 

    // Note that the method name is change. This is a method to modify the properties of ServerWebExchange. It returns a Builder instance, which is the internal class of ServerWebExchange
    default Builder mutate() {
         return new DefaultServerWebExchangeBuilder(this);
    }

    interface Builder {      
         
        // Overwrite ServerHttpRequest
        Builder request(Consumer<ServerHttpRequest.Builder> requestBuilderConsumer);
        Builder request(ServerHttpRequest request);
        
        // Override ServerHttpResponse
        Builder response(ServerHttpResponse response);
        
        // Overwrite the currently requested authenticated user
        Builder principal(Mono<Principal> principalMono);
    
        // Build a new ServerWebExchange instance
        ServerWebExchange build();
    }
}

GatewayFilter

Gateway Filter is a gateway Filter. Many frameworks have Filter design to realize extensible aspect logic.

GatewayFilter source code:

public interface GatewayFilter extends ShortcutConfigurable {

    String NAME_KEY = "name";
    String VALUE_KEY = "value";

    /**
     * Process the Web request and (optionally) delegate to the next
     * {@code WebFilter} through the given {@link GatewayFilterChain}.
     * @param exchange the current server exchange
     * @param chain provides a way to delegate to the next filter
     * @return {@code Mono<Void>} to indicate when request processing is complete
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

Finally, the filter is called in a chain through the filter chain. Each filter is delegated to the filter chain after processing the pre filter logic, and then the filter chain is delegated to the next filter.

GatewayFilterChain source code:

public interface GatewayFilterChain {

    /**
     * Delegate to the next {@code WebFilter} in the chain.
     * @param exchange the current server exchange
     * @return {@code Mono<Void>} to indicate when request handling is complete
     */
    Mono<Void> filter(ServerWebExchange exchange);

}

RouteLocator

RouteLocator is a route locator used to obtain route objects

public interface RouteLocator {
    Flux<Route> getRoutes();
}

RouteLocator has three implementation classes:

  • RouteDefinitionRouteLocator is a locator based on the route definition
  • Cacheingroutelocator cache based route locator
  • CompositeRouteLocator routing locator based on combination mode

RouteDefinitionLocator interface

RouteDefinitionLocator is the top-level interface of the route definition locator. Its main function is to read the route configuration information (org.springframework.cloud.gateway.route.RouteDefinition). It has five different implementation classes, as shown in figure

org.springframework.cloud.gateway.route.RouteDefinitionLocator, the route definition locator interface, has only one method, which is used to obtain the route definition list.

public interface RouteDefinitionLocator {

	Flux<RouteDefinition> getRouteDefinitions();
}

From the class diagram of RouteDefinitionLocator, you can see that the interface has multiple implementation classes:

  • PropertiesRouteDefinitionLocator: property based configuration
  • DiscoveryClientRouteDefinitionLocator: Based on service discovery
  • CompositeRouteDefinitionLocator: combination method
  • Cacheingroutedefinitionlocator: cache mode
  • There is also an interface RouteDefinitionRepository, which inherits from RouteDefinitionLocator and is used for operations on route definitions (saving and deleting route definitions). It has only one default implementation class, InMemoryRouteDefinitionRepository

RouteDefinition class

As the name suggests, this component is used to define Route information, which will eventually be parsed into Route by RouteLocator, and its properties are similar to those of Route class.

@Validated
public class RouteDefinition {
    private String id;
    @NotEmpty
    @Valid
    private List<PredicateDefinition> predicates = new ArrayList();
    @Valid
    private List<FilterDefinition> filters = new ArrayList();
    @NotNull
    private URI uri;
    private Map<String, Object> metadata = new HashMap();
    private int order = 0;
}

Initialize loading process

1. Route construction method

Spring provides two ways: externalizing configuration and programming.

Externalized configuration:

spring:
  cloud:
    gateway:
      enabled: true
      routes:
        - id: app-service001 # Route unique ID
          uri: http://localhost:9000 # destination URI,
          predicates:   # Assertion. If true, the match succeeds
            - Path=/app1/** # Configure the rule Path. If the request starts with app1, it will be forwarded to the target URL
          filters:
            - AddRequestHeader=X-Request-Foo, Bar  # A Filter is defined. When all requests are forwarded to downstream services, the request header X-Request-Foo:Bar will be added, which is produced by AddRequestHeaderGatewayFilterFactory.

Programming mode:

   @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/app1/**")
                        .filters(f -> f.filter(new RequestLogGatewayFilter()))
                        .uri("http://localhost:9000")
                )
                .build();
    }

2. Load configuration

Based on the automatic assembly function of Spring Boot, the automatic assembly class of Gateway module is GatewayAutoConfiguration, and the corresponding configuration class is GatewayProperties.

You can see that many Bean objects need to be used are injected into GatewayAutoConfiguration.

After configuring the route in yml, it will be resolved into a RouteDefinition object set when loaded. Each RouteDefinition contains Id, uri, predictions, filters, etc.

3. Load PropertiesRouteDefinitionLocator

PropertiesRouteDefinitionLocator is RouteDefinitionLocator, which is mainly used to obtain RouteDefinition (route definition information),

You can see that it is created directly using GatewayProperties,

    @Bean
    @ConditionalOnMissingBean
    public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
        return new PropertiesRouteDefinitionLocator(properties);
    }

Our routing information is stored in the PropertiesRouteDefinitionLocator Bean object.

4. Load RouteDefinitionRouteLocator

RouteDefinitionRouteLocator will also be loaded in the GatewayAutoConfiguration class. Be careful not to confuse it with the RouteDefinitionLocator above. The name is very similar to:

  • RouteDefinitionLocator: load the route as RouteDefinition through configuration, JAVA code and service discovery.
  • RouteDefinitionRouteLocator: used to convert RouteDefinition to Route.
    @Bean
    public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
        return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties, configurationService);
    }

You can see that the GatewayProperties, GatewayFilterFactory, RoutePredicateFactory, RouteDefinitionLocator and ConfigurationService are passed in using the RouteDefinitionRouteLocator constructor. Yes, there are 28 gateway filters and 13 assertion factories.

Its constructor initializes the relevant properties,

    public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties, ConfigurationService configurationService) {
    	// Set RouteDefinitionLocator
        this.routeDefinitionLocator = routeDefinitionLocator;
        // Setting ConfigurationService
        this.configurationService = configurationService;
        // Initialize predictions, put all routepredictefactories into one Map, and print the "loaded routepredictefactory + prefix log"
        this.initFactories(predicates);
        // Initialize the GatewayFilter and put all gatewayfilters into one Map
        gatewayFilterFactories.forEach((factory) -> {
            GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
        });
        // Set GatewayProperties
        this.gatewayProperties = gatewayProperties;
    }

Finally, the Route locator is initialized and injected into the Spring IOC, which maintains the Route information, filter, assertion factory and other information.

5. Load HandlerMapping and WebHandler

Before file In, we analyzed the execution process of the gateway,

The client sends a request to the Spring Cloud Gateway. The Gateway Handler Mapping determines the route matching the request and sends it to the Gateway Web Handler. The handler also sends the request to our actual service through the specified filter, executes the business logic, and then returns.

Therefore, during Gateway initialization, HandlerMapping and WebHandler will be loaded. This involves the relevant knowledge of Web flux, which will be described in detail later.

    @Bean
    public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
        return new FilteringWebHandler(globalFilters);
    }
    
    @Bean
    public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
        return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
    }

5. Start service

After all components are initialized, the service is started and completed. The web program listens to the port, receives external requests, passes through the core controller, processing mapper, web processor and filter, reaches the target address, returns all the way, and the whole process ends.

Reference documents

https://www.iocoder.cn/Spring-Cloud-Gateway/ouwenxue/intro/
https://www.cnblogs.com/fdzang/p/11812348.html

Tags: Java Spring Back-end Spring Cloud

Posted on Wed, 01 Dec 2021 08:13:53 -0500 by ktstowell