Principle Analysis of Execution Order of Spring Cloud Gateway Filter

Posted by methyl_blue on Sun, 06 Feb 2022 19:30:27 +0100

Filter type


GlobalFilter: A global filter that is valid for all routes. Create by implementing the GlobalFilter interface

GatewayFilter: Gateway filters, also known as local filters or custom filters, only work for routes that have this filter configured. Created through GatewayFilterFactory.

The filter is executed twice and is divided into pre and post s.

pre: Called before request.

post: Called when the response results are returned, in the exact opposite order to preprep. Only the preexecution order of the filter is discussed here, posts are inverted.

First come to the conclusion
conclusion


The online statements are not very accurate, in fact, there is not much fancy.

Ultimately, they all execute by sorting through the Order value, the smaller the Order value, the earlier it executes.

Source at org. Springframework. Cloud. Gateway. Handler. Inside the FilteringWebHandler#handle method

AnnotationAwareOrderComparator.sort(combined);

As to why some obscure sorting occurs, it is because the Order values are the same or the rules for generating Order values are not understood. These two points are explained below, followed by code validation and source analysis.

When Order values are the same


1. When two GlobalFilter types have the same filter Order value, the file name takes precedence over the others, sorted by the file name letter.

The reason is that packages are scanned in the order of files and then encapsulated into a List collection, and if the Order values are the same when sorting through the Order values, the file name will still be in the top place.

2.GlobalFilter types take precedence over GatewayFilter types when their filter Order values are the same.

The reason is that these two filters will eventually merge into a set of filters to form a chain of filter calls, from a list.addAll(); The method adds a filter of GatewayFilter type to the GlobalFilter filter collection, and addAll() is the last addition, so the filter of GatewayFilter type will come after when the Order value is the same.

Order Value Generation Rule


1.GlobalFilter type filter, set by implementing the getOrder() method of the Ordered interface.

2.GatewayFilter type filters, unable to set the Order value manually, are generated automatically by the order of filters configured in the configuration file, fixed from 1 encapsulated, for example, when three filters are configured, the Order values are 1, 2, 3 in the order from top to bottom.

Code Validation
Define GlobalFilter Filter

@Component
public class GaFilter implements GlobalFilter, Ordered {
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("Ga 1");
        return chain.filter(exchange);
    }
 
    @Override
    public int getOrder() {
        return 1;
    }
}
@Component
public class GbFilter implements GlobalFilter, Ordered {
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("Gb 2");
        return chain.filter(exchange);
    }
 
    @Override
    public int getOrder() {
        return 2;
    }
}
@Component
public class GcFilter implements GlobalFilter, Ordered {
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("Gc 1");
        return chain.filter(exchange);
    }
 
    @Override
    public int getOrder() {
        return 1;
    }
}

Define GatewayFilter Filter

Note: For simplicity, inherit directly from the AbstractNameValueGatewayFilterFactory and actually choose the inheritance class depending on your needs

@Component
public class FaGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            System.out.println("Fa");
            return chain.filter(exchange);
        };
    }
}
@Component
public class FbGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            System.out.println("Fb");
            return chain.filter(exchange);
        };
    }
}

@Component
public class FcGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            System.out.println("Fc");
            return chain.filter(exchange);
        };
    }
}

A filter of type GatewayFilter only works on routing services that have this filter configured, so it needs to be added to the routing configuration. The n, v after'='is the value inside NameValueConfig. Write it here.

      routes:
        - id: User Services
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
          filters:
            - Fa=n, v
            - Fc=n, v
            - Fb=n, v

Output of Results

Ga 1
Gc 1
Fa
Gb 2
Fc
Fb

Source Code Analysis

When the service starts, org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator loads the filters configured in the route into the set of filters for the route and encapsulates the Order value.

    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        List<GatewayFilter> filters = (List)filterDefinitions.stream().map((definition) -> {
            // Remove configured filters from the filter factory and encapsulate them in the set of filters for routing
            GatewayFilterFactory factory = (GatewayFilterFactory)this.gatewayFilterFactories.get(definition.getName());
            if (factory == null) {
                throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
            } else {
                Map<String, String> args = definition.getArgs();
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
                }
 
                Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
                Object configuration = factory.newConfig();
                ConfigurationUtils.bind(configuration, properties, factory.shortcutFieldPrefix(), definition.getName(), this.validator);
                GatewayFilter gatewayFilter = factory.apply(configuration);
                if (this.publisher != null) {
                    this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
                }
 
                return gatewayFilter;
            }
        }).collect(Collectors.toList());
        ArrayList<GatewayFilter> ordered = new ArrayList(filters.size());
        
        // Traverse the set of filters, encapsulated as a set of filters with an Order value
        for(int i = 0; i < filters.size(); ++i) {
            GatewayFilter gatewayFilter = (GatewayFilter)filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            } else {
                // Incrementally generate Order value from 1
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }
 
        return ordered;
    }

When a gateway is requested, org.springframework.cloud.gateway.handler.FilteringWebHandler combines filters in the route with global filters to encapsulate sorting and generate a complete filter chain.

    public Mono<Void> handle(ServerWebExchange exchange) {
        Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        List<GatewayFilter> gatewayFilters = route.getFilters();
        List<GatewayFilter> combined = new ArrayList(this.globalFilters);
        // Add the set of filters in the route to the global set of filters
        combined.addAll(gatewayFilters);
        // Sorting algorithm
        AnnotationAwareOrderComparator.sort(combined);
        if (logger.isDebugEnabled()) {
            logger.debug("Sorted gatewayFilterFactories: " + combined);
        }
 
        return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);
    }

debug debugging

Through debug debugging, the order of filters before sorting shows that the GatewayFilter type filters configured in the route are appended to the GlobalFilter filter collection, and that the filter Order values of GatewayFilter type are 1, 2, 3.

After sorting, sorted by Order value, GlobalFilter type filters have the same Order value, sorted by file name, and GatewayFilte and GlobalFilterr type filters have the same Order value under GlobalFilter.

So the final execution sequence is

You can debug your own project and view the org. Springframework. Cloud. Gateway. Handler. The combined order in FilteringWebHandler#handle is the filter execution order.


--------
Copyright Notice: This is an original article by CSDN blogger "Grab Hands". It is reproduced in accordance with the CC 4.0 BY-SA copyright agreement. Please attach a link to the original source and this statement.
Original Link: https://blog.csdn.net/Anenan/article/details/114691488

Topics: Spring Cloud gateway Cloud Native