Principle analysis of Spring Cloud Gateway filter execution order

Filter type

GlobalFilter: Global filter, effective for all routes. Created by implementing the GlobalFilter interface

GatewayFilter: Gateway filter, it can also be said to be a partial filter or a custom filter, which only takes effect for the route configured with this filter. Created by GatewayFilterFactory .

The filter will be executed twice, and the filter is divided into pre and post.

pre: Called before request.

post: Called when the response result is returned. The order is completely opposite to pre. Here we only discuss the pre execution order of the filter, and post is inverted.

 

Let's talk about the conclusion first

in conclusion

The statements on the Internet are not very accurate, but they are not too fancy.

In the end, they are all sorted and executed by the Order value, and the smaller the Order value is, the earlier it is executed.

The source code is in the org.springframework.cloud.gateway.handler.FilteringWebHandler#handle method

AnnotationAwareOrderComparator.sort(combined);

As for why there are some inexplicable sorting situations, they are all due to the same Order value or not understanding the rules for generating the Order value. The two points will be explained below first, and finally will be explained through code verification and source code analysis.

When the order value is the same

1. When the two GlobalFilter type filters have the same Order value, they are sorted alphabetically according to the file name, and the first file name has the higher priority.

The reason is that when the package is scanned, it is scanned in the order of the files, and then encapsulated into the List collection. When sorting by the Order value, if the Order value is the same, the file name in the first name will still be ranked first.

2. When the filter Order value of the GlobalFilter type and the GatewayFilter type is the same, the GlobalFilter type has a higher priority.

The reason is that these two filters will eventually be merged into a filter set to form a filter call chain. The source code is to add the GatewayFilter type filter to the GlobalFilter filter set through the list.addAll(); method, addAll() is The method is added at the end, so when the Order value is the same, the GatewayFilter type filter will be ranked behind.

Order value generation rules

1. The GlobalFilter type filter is set by the geOrder() method that implements the Ordered interface.

2. GatewayFilter type filter, the Order value cannot be manually set. It is automatically generated by the filter order configured in the configuration file. It is fixed starting from 1 and the package is fixed. For example, if three filters are configured, the Order value will be in order from top to bottom. 1, 2, 3.

 

Code verification

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 the GatewayFilter filter

Note: For the sake of simplicity, it is directly inherited from AbstractNameValueGatewayFilterFactory. Actual use depends on your own needs to choose the inherited class

@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);
        };
    }
}

The GatewayFilter type filter only takes effect on the routing service configured with this filter, so it needs to be added to the routing configuration. "="The n and v are the values ​​in the NameValueConfig, just write it here.

      routes:
        - id: 用户服务
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
          filters:
            - Fa=n, v
            - Fc=n, v
            - Fb=n, v

Result output

Ga 1
Gc 1
Fa
Gb 2
Fc
Fb

 

Source code analysis

When the service starts, org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator will load the filters configured in the route into the filter set of the route and encapsulate the Order value.

    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        List<GatewayFilter> filters = (List)filterDefinitions.stream().map((definition) -> {
            // 从过滤器工厂中取出配置了的过滤器封装到路由的过滤器集合中
            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());
        
        // 遍历过滤器集合,封装成带Order值的过滤器集合
        for(int i = 0; i < filters.size(); ++i) {
            GatewayFilter gatewayFilter = (GatewayFilter)filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            } else {
                // 从1开始递增生成Order值
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }

        return ordered;
    }

When requesting the gateway, org.springframework.cloud.gateway.handler.FilteringWebHandler will merge and encapsulate the filters in the route with the global filters to 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);
        // 将路由中的过滤器集合添加到全局过滤器集合
        combined.addAll(gatewayFilters);
        // 排序算法
        AnnotationAwareOrderComparator.sort(combined);
        if (logger.isDebugEnabled()) {
            logger.debug("Sorted gatewayFilterFactories: " + combined);
        }

        return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);
    }

debug

Through debug debugging, the filter order before sorting, you can see that the GatewayFilter type filter configured in the route is appended to the GlobalFilter filter set, and you can see that the GatewayFilter type filter Order value is 1, 2, 3.

After sorting, it is sorted according to the Order value. When the Order value of the GlobalFilter type filter is the same, it is sorted by the file name. When the Order value of the GatewayFilte and GlobalFilterr type filters are the same, it is also sorted under the GlobalFilter.

 

So the final execution order is

 

 You can debug in the project yourself, check the combined order in org.springframework.cloud.gateway.handler.FilteringWebHandler#handle, which is the filter execution order.

Guess you like

Origin blog.csdn.net/Anenan/article/details/114691488