The introduction

Spring Cloud Gateway dynamic routing and built-in filters. In the spirit of probing, I can’t help but wonder how Spring Cloud Gateway’s filters are loaded. We all know about WebFlux in the Spring 5.x release, which is part of the Spring 5.x framework and provides responsive programming support for Web applications. The Spring Cloud Gateway is implemented based on WebFlux.

So today we’ll take a look at how Spring Cloud Gateway fits in with Web Flxu.

Gateway point

We know that requests are handled and forwarded by DispatcherServlet in SpringMVC, and Spring WebFlux has a similar class: DispatcherHandler

ServerWebExchange gets the Handler instance from the handlerMappings property based on the current request information and calls it.

@Override public Mono<Void> handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return createNotFoundError(); } if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { return handlePreFlight(exchange); } return toggle. fromIterable(this.handlermappings) // getHandler instances. ConcatMap (mapping -> mapping.gethandler) .next().switchIfEmpty(createNotFoundError()) // Call the handler method. FlatMap (handler -> invokeHandler(exchange, FlatMap (result -> handleResult(exchange, result)); }Copy the code

Let’s look at the handlerMappings attribute, which is a List

List, and the value of the attribute is obtained from the Spring container using the initStrategies method.

protected void initStrategies(ApplicationContext context) { Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerMapping.class, true, false); ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values()); AnnotationAwareOrderComparator.sort(mappings); this.handlerMappings = Collections.unmodifiableList(mappings); . }Copy the code

Among the HandlerMapping instances, we can find one of the Spring Cloud Gateway implementation classes: RoutePredicateHandlerMapping, this class is passed in GatewayAutoConfiguration class @ Bean instantiation of the annotations, which also by constructor injection has an important example of this: RouteLocator.

Now, let’s go back to the handle method of the DispatcherHandler class, which returns some handler object by calling the HandlerApp #getHandler method, and then calling it.

So, among RoutePredicateHandlerMapping instance, getHandler method is actually call the parent class, which in turn calls the RoutePredicateHandlerMapping# getHandlerInternal method.

protected Mono<? > getHandlerInternal(ServerWebExchange exchange) { if (this.managementPortType == DIFFERENT && this.managementPort ! = null && exchange.getRequest().getURI().getPort() == this.managementPort) { return Mono.empty(); } exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); return lookupRoute(exchange) .flatMap((Function<Route, Mono<? >>) r -> { exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isDebugEnabled()) { logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r); } // Cache the matched routing instance into the context of the current request exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); // Return FilteringWebHandler instance 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) + "]"); }}))); }Copy the code

Will finally go the RoutePredicateHandlerMapping# lookupRoute method.

Protected Mono < Route > lookupRoute (ServerWebExchange exchange) {/ / traverse the Route list return this. RouteLocator. GetRoutes () .concatMap(route -> Mono.just(route).filterWhen(r -> { exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); // Determine whether the route matches return r.greetpredicate ().apply(exchange); }) .doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e)) .onErrorResume(e -> Mono.empty())) .next() .map(route -> { if (logger.isDebugEnabled()) { logger.debug("Route matched: " + route.getId()); } validateRoute(route, exchange); return route; }); }Copy the code

As you already know, the route is finally matched against the current request, cached in the context of the current request information, and returned with the corresponding FilteringWebHandler instance.

Finally, put a RoutePredicateHandlerMapping class diagram

Gateway Filter processing flow

From the previous section, we saw how Gateway gets into the WebFlux process. Now let’s look at how the filter framework for the Spring Cloud Gateway is handled.

Before we talk about the Spring Cloud Gateway filter process, we need to know that there are two types of filters in the Gateway:

  • GlobalFilterA filter of the common type that is loaded on all routes
  • GatewayFilterFor a special type of filter, only the displayed filter is mounted to a filter, the corresponding logical processing will be performed

In the last section, we saw that the DispatcherHandler#handle method ends up doing two things:

  1. The routing object is matched based on the request information and placed in the context of the current request
  2. returnFilteringWebHandlerThe instance

And then we go down and make a method call in the DispatcherHandler#invokeHandler method, which goes to FilteringWebHandler#handle.

public Mono<Void> handle(ServerWebExchange exchange) {
   Route route = exchange.getRequiredAttribute(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 DefaultGatewayFilterChain(combined).filter(exchange);
}
Copy the code

In this method, the Route object is retrieved from the context of the current request, the filters are assembled, the GlobalFilter and GatewayFilter are combined into a list, and then placed into the filter chain for logical processing.

conclusion

Through the above step by step combing, we finally understand how Spring Cloud Gateway is combined with WebFlux, and how the most important Filter works.

Above, if there is something wrong, welcome to discuss.

Add your personal wechat friends to discuss below: DaydayCoupons