[In-depth analysis of spring cloud gateway] Brief analysis of 06 gateway source code

The previous section made a very simple example. Microservices are registered to eureka, and then the gateway accesses the corresponding microservices through service discovery. This section will briefly analyze the entire gateway request forwarding process.

1. Core process

Insert image description here

Main process:

  • Gateway Client sends a request to Spring Cloud Gateway
  • The request will first be extracted and assembled into a gateway context by HttpWebHandlerAdapter
  • The context of the gateway is then passed to the DispatcherHandler, which is responsible for distributing the request to the RoutePredicateHandlerMapping
  • RoutePredicateHandlerMapping is responsible for route lookup and determining whether the route is available based on route assertions.
  • If the assertion is successful, the filter chain is created by FilteringWebHandler and called
  • Run the request through a chain of request-specific Filters. The reason why Filters are separated by a dotted line is that Filters can run logic before (pre) and after (post) sending the proxy request.
  • Executes all pre filter logic. Then make a proxy request. After the proxy request is made, the "post" filter logic is run.
  • Return the Response to the Gateway client after processing

2. Specific analysis

When the request comes, it will go through the HttpWebHandlerAdapter.handle method, which can be understood as the main entrance of the request

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

What is this delegate, look at the interface definition:
it is a processor, all parameters are encapsulated in the context exchange

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

}

Eventually it will be transferred to 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));
}

What is handlerMappings? It
is a list. HandlerMapping can find the corresponding processor based on the current request.
If the current request, such as /hello/world, is an interface corresponding to a controller on the gateway service, then this can find a RequestMappingHandlerAdapter through RequestMappingHandlerMapping.
If the current request needs to be forwarded to the downstream microservice, find the RoutePredicateHandlerMapping

RoutePredicateHandlerMapping to find the main logic of the route.

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

Let’s take a look at the specific way to find routes: It turns out that all routes are matched using predicate to find the route that matches the current request.

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()); }
     */
}

What routes does routeLocator contain? Debug to see
Insert image description here

It can be seen that after using service registration and discovery, in fact, a microservice will automatically register a route. For example, the hello-service above automatically registers a route with the path: /hello-service/**. This is why we can automatically forward the hello-service request without any routing configuration in our yml configuration file.
At the same time, you can see that there is a ReWritePathFilter under this route, which will automatically remove the service name and forward the request to the downstream microservice.
Insert image description here

Next, let’s take a look at the processing logic in 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);
}

You can see that there are two main logics in the code:
1. Take out the route from the context, which contains filters
2. Also take out the Global Filter in the spring container
3. Merge the above two filters and sort them
4. Assemble the filters list Make a call by forming a chain of responsibility.
You can go through the source code, and then look at the diagram of the core process, so that it will be clearer.
Another slightly more detailed diagram sums it up:
Insert image description here

My personal experience when looking at this source code: The entire core process is not complicated. The difficulty is probably reactor reactive programming. If you have never been exposed to this area before, you will feel confused about where to go next! So to learn this source code, you also have to learn reactor.

Guess you like

Origin blog.csdn.net/suyuaidan/article/details/132662044