Análisis de principio de la orden de ejecución del filtro Spring Cloud Gateway

Tipo de filtro

GlobalFilter: filtro global, efectivo para todas las rutas. Creado mediante la implementación de la interfaz GlobalFilter

GatewayFilter: filtro de puerta de enlace, que también se puede decir que es un filtro parcial o un filtro personalizado, que solo es efectivo para las rutas configuradas con este filtro. Creado por GatewayFilterFactory .

El filtro se ejecutará dos veces y el filtro se dividirá en pre y post.

pre: Llamado antes de la solicitud.

post: Se llama cuando se devuelve el resultado de la respuesta. El orden es completamente opuesto a pre. Aquí solo discutimos el orden de ejecución previa del filtro, y post se invierte.

 

Hablemos primero de la conclusión.

En conclusión

Las declaraciones en Internet no son muy precisas, pero tampoco demasiado sofisticadas.

Al final, todos se ordenan y ejecutan por el valor de la Orden, y cuanto menor es el valor de la Orden, antes se ejecuta.

El código fuente está en el método org.springframework.cloud.gateway.handler.FilteringWebHandler # handle

AnnotationAwareOrderComparator.sort(combined);

En cuanto a por qué hay algunas situaciones de clasificación inexplicables, todas se deben al mismo valor de Orden o al no entender las reglas para generar el valor de Orden. Los dos puntos se explicarán a continuación primero y, finalmente, se explicarán a través de la verificación del código y el análisis del código fuente.

Cuando el valor del pedido es el mismo

1. Cuando los dos filtros de tipo GlobalFilter tienen el mismo valor de Orden, se ordenan alfabéticamente según el nombre del archivo y el primer nombre de archivo tiene la prioridad más alta.

La razón es que cuando se escanea el paquete, se escanea en el orden de los archivos y luego se encapsula en la colección de Lista. Si el valor de Orden es el mismo al ordenar por el valor de Orden, el nombre de archivo en el primer nombre será todavía estar en el primer lugar.

2. Cuando el valor Order del filtro del tipo GlobalFilter y el tipo GatewayFilter es el mismo, el tipo GlobalFilter tiene una prioridad más alta.

La razón es que estos dos filtros eventualmente se fusionarán en un conjunto de filtros para formar una cadena de llamadas de filtro. El código fuente es agregar el filtro de tipo GatewayFilter al conjunto de filtros GlobalFilter a través del método list.addAll ();, addAll () is El método se agrega al final, por lo que cuando el valor de Order es el mismo, el filtro de tipo GatewayFilter se clasificará detrás.

Reglas de generación de valor de pedidos

1. El filtro de tipo GlobalFilter lo establece el método geOrder () que implementa la interfaz Ordered.

2. Filtro de tipo GatewayFilter, el valor de Orden no se puede configurar manualmente. Se genera automáticamente por la orden de filtro configurada en el archivo de configuración. Se fija a partir de 1 y el paquete es fijo. Por ejemplo, si se configuran tres filtros, el El valor del pedido estará en orden de arriba hacia abajo.1, 2, 3.

 

Verificación de código

Definir filtro GlobalFilter

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

Definir el filtro GatewayFilter

Nota: En aras de la simplicidad, se hereda directamente de AbstractNameValueGatewayFilterFactory. El uso real depende de sus propias necesidades para elegir la clase heredada

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

El filtro de tipo GatewayFilter solo tiene efecto en el servicio de enrutamiento configurado con este filtro, por lo que debe agregarse a la configuración de enrutamiento. "=" Los n y v son los valores en NameValueConfig, simplemente escríbalos aquí.

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

Salida de resultados

Ga 1
Gc 1
Fa
Gb 2
Fc
Fb

 

Análisis de código fuente

Cuando se inicia el servicio, org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator cargará los filtros configurados en la ruta en el conjunto de filtros de la ruta y encapsulará el valor de la orden.

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

Al solicitar la puerta de enlace, org.springframework.cloud.gateway.handler.FilteringWebHandler fusionará y encapsulará los filtros en la ruta con los filtros globales para generar una cadena de filtros completa.

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

depurar

A través de la depuración de errores, el orden de filtrado antes de la clasificación, puede ver que el filtro de tipo GatewayFilter configurado en la ruta se agrega al conjunto de filtros GlobalFilter, y puede ver que el valor de Orden de filtro de tipo GatewayFilter es 1, 2, 3.

Después de ordenar, se ordena según el valor de Orden. Cuando el valor de Orden del filtro de tipo GlobalFilter es el mismo, se ordena por el nombre de archivo. Cuando el valor de Orden de los filtros de tipo GatewayFilte y GlobalFilterr es el mismo, es también ordenados en GlobalFilter.

 

Entonces la orden de ejecución final es

 

 Puede depurar el proyecto usted mismo, verifique el orden combinado en org.springframework.cloud.gateway.handler.FilteringWebHandler # handle, que es el orden de ejecución del filtro.

Supongo que te gusta

Origin blog.csdn.net/Anenan/article/details/114691488
Recomendado
Clasificación