[Spring Cloud]Gateway - RouteLocator RouteDefinitionLocator FilteringWebHandler

Posted by spenceddd on Wed, 29 Dec 2021 14:40:49 +0100

preface

Record the Spring Cloud Gateway from the dimensions of routelocator, routedefinitionlocator, filteringwebhandler and related assembly classes

RouteLocator

public interface RouteLocator {

	Flux<Route> getRoutes();

}

The Route positioning class is generally configured with Spring Cloud Gateway. We will also construct the corresponding RouteLocator based on RouteLocatorBuilder, such as:

	@Bean
	public RouteLocator routeLocator(RouteLocatorBuilder builder) {

		return builder.routes()
				.route(p -> p
						.path("/client1/{segment}")
						.filters(f -> f.stripPrefix(1))
						.uri("http://localhost:8801/")
				)
				.build();
	}

Of course, it supports multi group route collection:

CachingRouteLocator

Cache the internal RouteLocator and refresh based on events

CompositeRouteLocator

Combination: combine multiple groups of routelocators into one

RouteDefinitionRouteLocator

	@Override
	public Flux<Route> getRoutes() {

		/**
		 * Get all routedefinitions based on routedefinionlocator
		 * 		Convert to Route instance
		 */
		Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute);

		// ...

		return routes.map(route -> {
			if (logger.isDebugEnabled()) {
				logger.debug("RouteDefinition matched: " + route.getId());
			}
			return route;
		});
	}
  • Obtain all routedefinitions based on routedefinionlocator and convert them into Route instances
  • It can be seen that only this class and the anonymous instance constructed based on RouteLocatorBuilder in the configuration class mentioned earlier are really responsible for route resolution

Assembly class GatewayAutoConfiguration

	@Bean
	public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
			List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates,
			RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
		return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties,
				configurationService);
	}

	@Bean
	@Primary
	@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
	public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
		/**
		 * Combine all RouteLocator instances and cache
		 */
		return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
	}

In the automatic assembly class GatewayAutoConfiguration:

  • RouteDefinitionRouteLocator instance registered
  • Collect all RouteLocator instances in the container (actually the two described above), Composite and Caching, and register the cacheingroutelocator instance
  • Because of the @ Primary annotation, the cachedCompositeRouteLocator instance is mainly used in the container later

RouteDefinitionLocator

public interface RouteDefinitionLocator {

	Flux<RouteDefinition> getRouteDefinitions();

}
  • As mentioned earlier, RouteDefinitionRouteLocator obtains all routedefinitions based on routeDefinitionLocator and converts them into Route instances
  • RouteDefinitionLocator, a Route definition locator, is used to collect multiple groups of routedefinitions, which are finally handed over to RouteDefinitionRouteLocator and converted into Route

CachingRouteDefinitionLocator

Cache the internal RouteDefinitionLocator and refresh based on events

CompositeRouteDefinitionLocator

It combines a set of routedefinitionlocators, so the getRouteDefinitions method emits all RouteDefinition elements

InMemoryRouteDefinitionRepository

Based on a set of routedefinitions for memory management, the save delete method is provided. By default, an instance is provided in the automatic assembly class, which can be overwritten by itself, as follows:

	@Bean
	public RouteDefinitionRepository repository() {
		PredicateDefinition predicateDefinition = new PredicateDefinition("Path=/eureka-client/{segement}");

		FilterDefinition filterDefinition = new FilterDefinition("StripPrefix=1");

		RouteDefinition routeDefinition = new RouteDefinition();
		routeDefinition.setId("test-repo");
		routeDefinition.setPredicates(Arrays.asList(new PredicateDefinition[] { predicateDefinition }));
		routeDefinition.setFilters(Arrays.asList(new FilterDefinition[] { filterDefinition }));
		routeDefinition.setUri(URI.create("http://localhost:8801"));

		InMemoryRouteDefinitionRepository repository = new InMemoryRouteDefinitionRepository();

		Mono<RouteDefinition> route = Mono.fromSupplier(() -> routeDefinition);
		
		// You must subscribe here. I don't know the reason @ TODO
		repository.save(route).subscribe();
		return repository;
	}

PropertiesRouteDefinitionLocator

	private final GatewayProperties properties;
	
	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {
		return Flux.fromIterable(this.properties.getRoutes());
	}

Obtaining based on GatewayProperties is actually reading based on the configuration file, similar to:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: test
          uri: http://localhost:8801
          predicates:
            - Path=/client/{segement}
          filters:
            - StripPrefix=1

DiscoveryClientRouteDefinitionLocator

This class generates a default RouteDefinition for each service based on service discovery, matches the route based on pathroutepredictefactory, and rewrites the path as lb:// + serviceId based on RewritePathGatewayFilterFactory

Assembly class

GatewayAutoConfiguration

	@Bean
	@ConditionalOnMissingBean
	public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
		return new PropertiesRouteDefinitionLocator(properties);
	}

	@Bean
	@ConditionalOnMissingBean(RouteDefinitionRepository.class)
	public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
		return new InMemoryRouteDefinitionRepository();
	}

	@Bean
	@Primary
	public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
		return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
	}
  • Register PropertiesRouteDefinitionLocator based on profile resolution
  • Register memory management based InMemoryRouteDefinitionRepository
  • CompositeRouteDefinitionLocator manages all of the above routedefinitionlocators, marked @ Primary, and can be directly injected into the container

GatewayDiscoveryClientAutoConfiguration

	@Bean
	public DiscoveryLocatorProperties discoveryLocatorProperties() {
		DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
		properties.setPredicates(initPredicates());
		properties.setFilters(initFilters());
		return properties;
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled", matchIfMissing = true)
	public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {

		@Bean
		@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
		public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
				ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
			return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
		}

	}
  • The configuration service finds the default route matcher and filter, that is, the pathroutepredictefactory and RewritePathGatewayFilterFactory mentioned earlier
  • Register the DiscoveryClientRouteDefinitionLocator instance based on the configuration item spring cloud. gateway. discovery. locator. enabled=true

FilteringWebHandler

	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);

		/**
		 * Obtain interceptors from the route instance, including default filters
		 */
		List<GatewayFilter> gatewayFilters = route.getFilters();

		/**
		 * Then add globalFilters
		 */
		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters);

		// Sorting based on Order interface
		AnnotationAwareOrderComparator.sort(combined);

		if (logger.isDebugEnabled()) {
			logger.debug("Sorted gatewayFilterFactories: " + combined);
		}

		// Intercept operation
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}
  • All filters in the Route instance will be obtained here, including default filters (specified in the configuration file, configuration item spring. Cloud. Gateway. Default filters)
  • It will also add globalFilters in all containers. Many are registered in the assembly class by default, omitted
  • Finally, intercept

Assembly class GatewayAutoConfiguration

	@Bean
	public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
		return new FilteringWebHandler(globalFilters);
	}

summary

Relevant records of Spring Cloud Gateway source code reading

Topics: Spring Spring Cloud