[Análisis en profundidad de Spring Cloud Gateway] Breve análisis del código fuente de 06 Gateway

La sección anterior dio un ejemplo muy simple: el microservicio se registra en eureka y luego la puerta de enlace accede al microservicio correspondiente a través del descubrimiento de servicios. Esta sección analizará brevemente todo el proceso de reenvío de solicitudes de puerta de enlace.

1. Proceso central

Insertar descripción de la imagen aquí

Proceso principal:

  • Gateway Client envía una solicitud a Spring Cloud Gateway
  • Primero, HttpWebHandlerAdapter extraerá la solicitud y la ensamblará en un contexto de puerta de enlace.
  • Luego, el contexto de la puerta de enlace se pasa al DispatcherHandler, que es responsable de distribuir la solicitud al RoutePredicateHandlerMapping.
  • RoutePredicateHandlerMapping es responsable de buscar rutas y determinar si la ruta está disponible en función de las afirmaciones de ruta.
  • Si la afirmación es exitosa, FilteringWebHandler crea la cadena de filtros y la llama
  • Ejecute la solicitud a través de la cadena de filtro específica de la solicitud. La razón por la que el filtro está separado por una línea de puntos es que el filtro puede ejecutar lógica antes (pre) y después (post) de enviar la solicitud de proxy.
  • Ejecute toda la lógica de prefiltro. Luego haga una solicitud de proxy. Después de realizar una solicitud de proxy, se ejecuta la lógica del filtro "post".
  • Después del procesamiento, la respuesta se devuelve al cliente de puerta de enlace.

2. Análisis específico

Cuando llega una solicitud, pasará por el método HttpWebHandlerAdapter.handle, que puede entenderse como la entrada principal de la solicitud.

@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
    
    
    if (this.forwardedHeaderTransformer != null) {
    
    
        try {
    
    
            request = this.forwardedHeaderTransformer.apply(request);
        }
        catch (Throwable ex) {
    
    
            if (logger.isDebugEnabled()) {
    
    
                logger.debug("Failed to apply forwarded headers to " + formatRequest(request), ex);
            }
            response.setStatusCode(HttpStatus.BAD_REQUEST);
            return response.setComplete();
        }
    }
 //组装上下文
    ServerWebExchange exchange = createExchange(request, response);

    LogFormatUtils.traceDebug(logger, traceOn ->
            exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +
                    (traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));
//委派给delegate来处理
    return getDelegate().handle(exchange)
            .doOnSuccess(aVoid -> logResponse(exchange))
            .onErrorResume(ex -> handleUnresolvedError(exchange, ex))
            .then(Mono.defer(response::setComplete));
}

¿Qué es este delegado? Eche un vistazo a la definición de la interfaz:
es un procesador y todos los parámetros están encapsulados en el intercambio de contexto.

public interface WebHandler {
    
    

    /**
     * Handle the web server exchange.
     * @param exchange the current server exchange
     * @return {@code Mono<Void>} to indicate when request handling is complete
     */
    Mono<Void> handle(ServerWebExchange exchange);

}

Eventualmente será transferido a DispatcherHandler

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    
    
    if (this.handlerMappings == null) {
    
    
        return createNotFoundError();
    }
    if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
    
    
        return handlePreFlight(exchange);
    }
    return Flux.fromIterable(this.handlerMappings)
            .concatMap(mapping -> mapping.getHandler(exchange))
            .next()
            .switchIfEmpty(createNotFoundError())
            .flatMap(handler -> invokeHandler(exchange, handler))
            .flatMap(result -> handleResult(exchange, result));
}

¿Qué es handlerMappings? Es
una lista. HandlerMapping puede encontrar el procesador correspondiente de acuerdo con la solicitud actual.
Si la solicitud actual como /hello/world es una interfaz correspondiente al controlador en el servicio de puerta de enlace, entonces esto puede encontrar un RequestMappingHandlerAdapter a través de RequestMappingHandlerMapping.
Si la solicitud actual debe reenviarse al microservicio posterior, busque RoutePredicateHandlerMapping

RoutePredicateHandlerMapping para encontrar la lógica principal del enrutamiento.

@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
    
    
    // don't handle requests on management port if set and different than server port
    if (this.managementPortType == DIFFERENT && this.managementPort != null
            && exchange.getRequest().getLocalAddress() != null
            && exchange.getRequest().getLocalAddress().getPort() == this.managementPort) {
    
    
        return Mono.empty();
    }
    exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());

    return lookupRoute(exchange)
            // .log("route-predicate-handler-mapping", Level.FINER) //name this
            .flatMap((Function<Route, Mono<?>>) r -> {
    
    
                exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                if (logger.isDebugEnabled()) {
    
    
                    logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
                }
                                //把找到的路由放到exchange上下文中
                exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
                                //返回的handler实际上是webHandler
                return Mono.just(webHandler);
            }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
    
    
                exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                if (logger.isTraceEnabled()) {
    
    
                    logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
                }
            })));
}

Echemos un vistazo a la forma específica de encontrar rutas: resulta que todas las rutas coinciden usando el predicado para encontrar la ruta que coincide con la solicitud actual.

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
    
    
    return this.routeLocator.getRoutes()
            // individually filter routes so that filterWhen error delaying is not a
            // problem
            .concatMap(route -> Mono.just(route).filterWhen(r -> {
    
    
                // add the current route we are testing
                exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                //用predicate作一下匹配,找出符合当前请求的路由
                        return r.getPredicate().apply(exchange);
            })
                    // instead of immediately stopping main flux due to error, log and
                    // swallow it
                    .doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
                    .onErrorResume(e -> Mono.empty()))
            // .defaultIfEmpty() put a static Route not found
            // or .switchIfEmpty()
            // .switchIfEmpty(Mono.<Route>empty().log("noroute"))
            .next()
            // TODO: error handling
            .map(route -> {
    
    
                if (logger.isDebugEnabled()) {
    
    
                    logger.debug("Route matched: " + route.getId());
                }
                validateRoute(route, exchange);
                return route;
            });

    /*
     * TODO: trace logging if (logger.isTraceEnabled()) {
     * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
     */
}

¿Qué rutas contiene routeLocator? Depurar para ver
Insertar descripción de la imagen aquí

Se puede ver que después de usar el registro y descubrimiento de servicios, de hecho, un microservicio registrará automáticamente una ruta, como el servicio de saludo anterior, que registra automáticamente una ruta con la ruta: /hello-service/**. Es por eso que podemos reenviar automáticamente solicitudes de servicio de saludo aunque no haya ningún enrutamiento configurado en nuestro archivo de configuración yml.
Al mismo tiempo, puede ver que hay un ReWritePathFilter en esta ruta, que eliminará automáticamente el nombre del servicio y reenviará la solicitud al microservicio posterior.
Insertar descripción de la imagen aquí

A continuación, echemos un vistazo a la lógica de procesamiento en FilteringWebHandler.

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    
    
//从上下文中取出路由,路由中包含filters
    Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
    List<GatewayFilter> gatewayFilters = route.getFilters();
//spring容器中的Global Filter也取出来
    List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
    combined.addAll(gatewayFilters);
    // TODO: needed or cached?
 //做个排序
    AnnotationAwareOrderComparator.sort(combined);

    if (logger.isDebugEnabled()) {
    
    
        logger.debug("Sorted gatewayFilterFactories: " + combined);
    }
//后面就是filter链式调用了
    return new DefaultGatewayFilterChain(combined).filter(exchange);
}

Se puede ver que hay dos lógicas principales en el código:
1. Saque la ruta del contexto y la ruta contiene filtros
2. El filtro global en el contenedor de resorte también se elimina
3. Fusione los dos filtros anteriores y ordénelos
4. Reúna la lista de filtros Para realizar llamadas en una cadena de responsabilidad
, puede revisar el código fuente y observar el diagrama del proceso central, que lo aclarará.
Resumiendo otro diagrama un poco más detallado:
Insertar descripción de la imagen aquí

Mi experiencia personal al mirar este fragmento de código fuente: todo el proceso central no es complicado y la dificultad probablemente sea la programación sensible al reactor. Si no ha tocado este fragmento antes, se sentirá confundido cuando no sepa dónde hacerlo. ¡siguiente! Entonces, para aprender este código fuente, también debes aprender reactor.

Supongo que te gusta

Origin blog.csdn.net/suyuaidan/article/details/132662044
Recomendado
Clasificación