1. Core Concepts

Routing the Route

Routing is the core abstraction of a gateway.

Public class Route implements Ordered {// unique ID private final String ID; // Redirect URI private final URI URI; // SpringBean priority private final int order; // Predicate private final AsyncPredicate<ServerWebExchange> predicate; Private final List<GatewayFilter> gatewayFilters; Private final Map<String, Object> metadata; }Copy the code

ServerWebExchange

ServerWebExchange is a spring-Web interface that provides get methods for ServerHttpRequest, ServerHttpResponse, ApplicationContext, and other instances.

Assertions AsyncPredicate

Assertion is used to determine whether a route matches a ServerWebExchange.

public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {// Construct and AsyncPredicate default AsyncPredicate<T> and(AsyncPredicate<? super T> other) { return new AndAsyncPredicate<>(this, other); // Construct non-asyncpredicate default AsyncPredicate<T> negate() {return new NegateAsyncPredicate<>(this); // Construct non-asyncpredicate default AsyncPredicate<T> negate() {return new NegateAsyncPredicate<>(this); } // Construct or AsyncPredicate default AsyncPredicate<T> or(AsyncPredicate<? super T> other) { return new OrAsyncPredicate<>(this, other); } // Construct default AsyncPredicate static AsyncPredicate<ServerWebExchange> from(Predicate<? super ServerWebExchange> predicate) { return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate)); // DefaultAsyncPredicate class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {private final Predicate<T> delegate;  public DefaultAsyncPredicate(Predicate<T> delegate) { this.delegate = delegate; } @Override public Publisher<Boolean> apply(T t) { return Mono.just(delegate.test(t)); // Non-asyncpredicate class NegateAsyncPredicate<T> implements AsyncPredicate<T> {private final AsyncPredicate<? super T> predicate; public NegateAsyncPredicate(AsyncPredicate<? super T> predicate) { this.predicate = predicate; } @Override public Publisher<Boolean> apply(T t) { return Mono.from(predicate.apply(t)).map(b -> ! b); // AsyncPredicate class AndAsyncPredicate<T> implements AsyncPredicate<T> {private final AsyncPredicate<? super T> left; private final AsyncPredicate<? super T> right; public AndAsyncPredicate(AsyncPredicate<? super T> left, AsyncPredicate<? super T> right) { this.left = left; this.right = right; } @Override public Publisher<Boolean> apply(T t) { return Mono.from(left.apply(t)).flatMap( result -> ! result ? Mono.just(false) : Mono.from(right.apply(t))); }} // OrAsyncPredicate class OrAsyncPredicate<T> implements AsyncPredicate<T> {private final AsyncPredicate<? super T> left; private final AsyncPredicate<? super T> right; public OrAsyncPredicate(AsyncPredicate<? super T> left, AsyncPredicate<? super T> right) { this.left = left; this.right = right; } @Override public Publisher<Boolean> apply(T t) { return Mono.from(left.apply(t)).flatMap( result -> result ? Mono.just(true) : Mono.from(right.apply(t))); }}}Copy the code

Route filter GatewayFilter

A filter for a Route cannot exist without a Route.

public interface GatewayFilter extends ShortcutConfigurable {
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
Copy the code

GlobalFilter GlobalFilter

A global filter that must be applied to all routes. During subsequent Bean instantiation, it will be used as a GatewayFilter.

public interface GlobalFilter {
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
Copy the code

Filter chain GatewayFilterChain

GatewayFilterChain Allows the GatewayFilter to be executed one by one.

public interface GatewayFilterChain {
	Mono<Void> filter(ServerWebExchange exchange);
}
Copy the code

RouteDefinition RouteDefinition

Similar to Spring’s BeanDefinition, one way to build a Route is through a RouteDefinition, such as a Route rule definition parsed from a properties file.

Public class RouteDefinition {private String id; Private List<PredicateDefinition> predicates = new ArrayList<>(); Private List<FilterDefinition> filters = new ArrayList<>(); // Redirect URI private uri URI; Private Map<String, Object> metadata = new HashMap<>(); private Map<String, Object> metadata = new HashMap<>(); Private int order = 0; }Copy the code

The assertion defines the PredicateDefinition

Predicate definitions loaded from the configuration file are converted to AsyncPredicate using RoutePredicateFactory#applyAsync when constructing the Route.

public class PredicateDefinition { private String name; private Map<String, String> args = new LinkedHashMap<>(); public PredicateDefinition() { } // predicates: Args public PredicateDefinition(String text) {int eqIdx = text.indexof ('='); // setName setName(text.substring(0, eqIdx)); String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); // set args for (int I = 0; i < args.length; i++) { this.args.put(NameUtils.generateName(i), args[i]); }}}Copy the code

The route filter defines FilterDefinition

A Route filter definition loaded from the configuration file is converted to a GatewayFilter using GatewayFilterFactory#apply when constructing a Route.

public class FilterDefinition { private String name; private Map<String, String> args = new LinkedHashMap<>(); Public FilterDefinition() {} PrefixPath=/httpbin public FilterDefinition(String text) {int eqIdx = text.indexOf('='); if (eqIdx <= 0) { setName(text); return; } setName(text.substring(0, eqIdx)); String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ","); for (int i = 0; i < args.length; i++) { this.args.put(NameUtils.generateName(i), args[i]); }}}Copy the code

Gets the route definition RouteDefinitionLocator

RouteDefinitionLocator Indicates the ability to obtain route definitions.

public interface RouteDefinitionLocator {
	Flux<RouteDefinition> getRouteDefinitions();
}
Copy the code

Is finally exposed outside the actual CompositeRouteDefinitionLocator, he combines all RouteDefinitionLocator.

Gets the route RouteLocator

A RouteLocator has the ability to get a Route Route. The gateway only calls the RouteLocator to get the Route when processing the request, and processes the request through the Route’s assertion and filtering.

public interface RouteLocator {
	Flux<Route> getRoutes();
}
Copy the code

When the Spring container loads, it puts all the routes into the Cachin Group Locator, and the subsequent runtime will only deal with the Cachin Group Locator.

2. Load routes

Explains assembly of beans from the Gateway to loading a Route into the CachingRouteLocator.

Automatic configuration

Spring – Cloud – Gateway automatically configure class is org. Springframework. Cloud. Gateway. Config. GatewayAutoConfiguration. Focus on some important Bean assemblies, global filters, etc., which will be discussed in the next section on the request process.

Gateway configuration file

@Bean public GatewayProperties gatewayProperties() { return new GatewayProperties(); } @configurationProperties (" Spring.cloud.gateway ") @validated Public class GatewayProperties {// Route private List<RouteDefinition> routes = new ArrayList<>(); Private List<FilterDefinition> defaultFilters = new ArrayList<>(); }Copy the code

RouteDefinitionLocator related

  • Create RouteDefinitionLocator PropertiesRouteDefinitionLocator: configuration file
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
        GatewayProperties properties) {
    return new PropertiesRouteDefinitionLocator(properties);
}
Copy the code
  • InMemoryRouteDefinitionRepository: memory level route definition, cannot persist, support the dynamic addition and deletion route definition
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
    return new InMemoryRouteDefinitionRepository();
}
Copy the code
  • CompositeRouteDefinitionLocator: all other RouteDefinitionLocator combined, and is the Primary
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(
        List<RouteDefinitionLocator> routeDefinitionLocators) {
    return new CompositeRouteDefinitionLocator(
            Flux.fromIterable(routeDefinitionLocators));
}
Copy the code

RouteLocator related

  • RouteLocatorBuilder: AIDS in encoding the injection of custom Routelocators.
@Bean
public RouteLocatorBuilder routeLocatorBuilder(
        ConfigurableApplicationContext context) {
    return new RouteLocatorBuilder(context);
}
Copy the code
  • RouteDefinitionRouteLocator: with RouteDefinitionLocator and GatewayFilterFactory and RoutePredicateFactory Route structure, create RouteLocator.
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
        List<GatewayFilterFactory> gatewayFilters,
        List<RoutePredicateFactory> predicates,
        RouteDefinitionLocator routeDefinitionLocator,
        ConfigurationService configurationService) {
    return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
            gatewayFilters, properties, configurationService);
}
Copy the code
  • CachingRouteLocator: The CompositeRouteLocator aggregates all other Routelocators to implement the RouteLocator.
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
    return new CachingRouteLocator(
            new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
Copy the code

Load the route entry class RouteRefreshListener

@Bean
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
    return new RouteRefreshListener(publisher);
}
Copy the code

Load a route to CachingRouteLocator

RouteRefreshListener

Load the entry to the RouteLocator, triggered by a Spring event.

public class RouteRefreshListener implements ApplicationListener<ApplicationEvent> { private final ApplicationEventPublisher publisher; public RouteRefreshListener(ApplicationEventPublisher publisher) { this.publisher = publisher; } @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent || event  instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) { reset(); } / /... Omit other event judgments, May also trigger the reset} / / release events RefreshRoutesEvent private void reset () {this. Publisher. PublishEvent (new RefreshRoutesEvent (this));  }}Copy the code

CachingRouteLocator

CachingRouteLocator receives RefreshRoutesEvent and delegates the CompositeRouteLocator to fetch Flux

into its cache cache.

public class CachingRouteLocator implements Ordered, RouteLocator, ApplicationListener<RefreshRoutesEvent>, ApplicationEventPublisherAware {/ / write dead cache cache key private static final String CACHE_KEY = "routes"; // CompositeRouteLocator private final RouteLocator delegate; Private final Flux<Route> routes; Private final Map<String, List> cache = new ConcurrentHashMap<>(); / / event publisher private ApplicationEventPublisher ApplicationEventPublisher; public CachingRouteLocator(RouteLocator delegate) { this.delegate = delegate; // Fetch is not triggered here, fetch is only triggered when routes are needed, For example, getRoutes() routes = cacheflux.lookup (cache, CACHE_KEY, route.class).onCachemissResume (this::fetch); } // Assign a CompositeRouteLocator to fetch a Route and sort private Flux<Route> fetch() {return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE); @override public Flux<Route> getRoutes() {return this.routes; } public Flux<Route> refresh() {this.cache.clear(); return this.routes; } // Receive refreshRoutesEvent@override public void onApplicationEvent(RefreshRoutesEvent Event) {try Fetch ().collect(Collectors. ToList ()).subscribe(list -> Flux. Fromititemize (list) .materialize().collect(Collectors.toList()).subscribe(signals -> { applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this)); Put (CACHE_KEY, signals); }, throwable -> handleRefreshError(throwable))); } catch (Throwable e) { handleRefreshError(e); Abnormal release RefreshRoutesResultEvent incident}} / / private void handleRefreshError (Throwable Throwable) {applicationEventPublisher  .publishEvent(new RefreshRoutesResultEvent(this, throwable)); } void handleRefresh() { refresh(); } @Override public int getOrder() { return 0; } @Override public void setApplicationEventPublisher( ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; }}Copy the code

CompositeRouteLocator

Get all routes from other Routelocators.

Public class CompositeRouteLocator implements RouteLocator {// All routelocators, In addition to CachingRouteLocator private final Flux<RouteLocator> delegates; public CompositeRouteLocator(Flux<RouteLocator> delegates) { this.delegates = delegates; } @Override public Flux<Route> getRoutes() { return this.delegates.flatMapSequential(RouteLocator::getRoutes); }}Copy the code

1 Encoding method Obtain Route

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route(r -> r.host("**.abc.org").and().path("/anything/png")
                .filters(f ->
                        f.prefixPath("/httpbin")
                                .addResponseHeader("X-TestHeader", "foobar"))
                .uri(uri)
            )
            .build();
}
Copy the code

RouteLocatorBuilder. Builder to construct RouteLocator, Bean injection structure directly when finished. RoutePredicateFactory and GatewayFilterFactory are also used to construct the RoutePredicateFactory.

public static class Builder {
	private List<Route.AsyncBuilder> routes = new ArrayList<>();
    public RouteLocator build() {
        return () -> Flux.fromIterable(this.routes)
                .map(routeBuilder -> routeBuilder.build());
    }
Copy the code

2 RouteDefinitionRouteLocator

RouteDefinitionRouteLocator only responsible for create the Route through RouteDefinition, entrust CompositeRouteDefinitionLocator RouteDefinition, ConfigurationService, RoutePredicateFactory, GatewayFilterFactory converts RouteDefinition to Route.

public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {/ / the default filter name public static final String DEFAULT_FILTERS = "defaultFilters"; / / entrust CompositeRouteDefinitionLocator private final RouteDefinitionLocator RouteDefinitionLocator; ConfigurationService Operates RoutePredicateFactory and GatewayFilterFactory. // Converts assertions and filters to private Final ConfigurationService configurationService; // name - RoutePredicateFactory private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>(); // name - GatewayFilterFactory private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();  // Spring.cloud. gateway configuration file private final GatewayProperties GatewayProperties; @Override public Flux<Route> getRoutes() { Flux<Route> routes = this.routeDefinitionLocator // Entrust CompositeRouteDefinitionLocator get RouteDefinition. GetRouteDefinitions () / / into the Route map (this: : convertToRoute); / / if the spring cloud. Gateway. FailOnRouteDefinitionError = true (default) / / print log only if (! gatewayProperties.isFailOnRouteDefinitionError()) { routes = routes.onErrorContinue((error, obj) -> { logger.warn(...) ; }); } return routes; }}Copy the code
  • CompositeRouteDefinitionLocator

GetRouteDefinitions loop All RouteDefinitionLocator Gets all RouteDefinitions.

public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator { private final Flux<RouteDefinitionLocator> delegates; private final IdGenerator idGenerator; public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) { this(delegates, new AlternativeJdkIdGenerator()); } public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates, IdGenerator idGenerator) { this.delegates = delegates; this.idGenerator = idGenerator; } @Override public Flux<RouteDefinition> getRouteDefinitions() { return this.delegates // Delegate all other RouteDefinitionLocator get RouteDefinition. FlatMapSequential (RouteDefinitionLocator: : getRouteDefinitions) . FlatMap (routeDefinition -> {// If routeDefinition does not set id, If (routedefine.getid () == null) {return randomId().map(id -> {routedefine.setid (id); return routeDefinition; }); } return Mono.just(routeDefinition); }); } // get id protected Mono<String> randomId() {return mono.fromsupplier (idGenerator::toString) .publishOn(Schedulers.boundedElastic()); }}Copy the code

There are many kinds of, RouteDefinitionLocator PropertiesRouteDefinitionLocator, for example.

public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator { private final GatewayProperties properties; public PropertiesRouteDefinitionLocator(GatewayProperties properties) { this.properties = properties; } @Override public Flux<RouteDefinition> getRouteDefinitions() { return Flux.fromIterable(this.properties.getRoutes()); }}Copy the code
  • RouteDefinitionRouteLocator.convertToRoute

RouteDefinition is converted to a Route through ConfigurationService, RoutePredicateFactory, and GatewayFilterFactory.

Private Route convertToRoute(RouteDefinition RouteDefinition) {// PredicateDefinition in RouteDefinition // Convert to AsyncPredicate via RoutePredicateFactory. AsyncPredicate<ServerWebExchange> Predicate = combinePredicates(routeDefinition); List<GatewayFilter> gatewayFilters = getFilters(routeDefinition); // Assemble Route return route.async (routeDefinition).asyncpredicate (predicate).replacefilters (gatewayFilters).build(); }Copy the code

RouteDefinitionRouteLocator# combinePredicates conversion PredicateDefinition AsyncPredicate.

private AsyncPredicate<ServerWebExchange> combinePredicates( RouteDefinition routeDefinition) { List<PredicateDefinition> predicates = routeDefinition.getPredicates(); // Get the first assertion definition, Predicate AsyncPredicate<ServerWebExchange> Predicate = lookup(routeDefinition, predicates. Get (0)); // Connect for (PredicateDefinition andPredicate: predicates.subList(1, predicates.size())) { AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate); predicate = predicate.and(found); } return predicate; } private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition, predicate) {PredicateDefinition name, predicateFactory, RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName()); / / ConfigurationService RouteDefinition operation and RoutePredicateFactory Object config = this. ConfigurationService. With (factory) .name(predicate.getName()) .properties(predicate.getArgs()) .eventFunction((bound, properties) -> new PredicateArgsEvent( RouteDefinitionRouteLocator.this, route.getId(), properties)) .bind(); return factory.applyAsync(config); }Copy the code

RouteDefinitionRouteLocator# getFilters converts FilterDefinition GatewayFilter.

private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) { List<GatewayFilter> filters = new ArrayList<>(); // If the default filter is not empty, add the default filter if (! this.gatewayProperties.getDefaultFilters().isEmpty()) { filters.addAll(loadGatewayFilters(DEFAULT_FILTERS, new ArrayList<>(this.gatewayProperties.getDefaultFilters()))); } // If the routeDefinition filter is not empty, add the filter if (! routeDefinition.getFilters().isEmpty()) { filters.addAll(loadGatewayFilters(routeDefinition.getId(), new ArrayList<>(routeDefinition.getFilters()))); } / / sorting AnnotationAwareOrderComparator. Sort (filters); return filters; } List<GatewayFilter> loadGatewayFilters(String id,List<FilterDefinition> filterDefinitions) { ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size()); for (int i = 0; i < filterDefinitions.size(); FilterDefinition definition = filterDefinitions. Get (I); / / according to the name of the definition of the Filter to obtain GatewayFilterFactory GatewayFilterFactory factory = this. GatewayFilterFactories .get(definition.getName()); / / configuration Object configuration = this. ConfigurationService. With (factory). The name (definition. The getName ()) .properties(definition.getArgs()) .eventFunction((bound, properties) -> new FilterArgsEvent( RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties)) .bind(); // convert to GatewayFilter GatewayFilter GatewayFilter = factory.apply(configuration); if (gatewayFilter instanceof Ordered) { ordered.add(gatewayFilter); } else { ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1)); } } return ordered; }Copy the code

conclusion

  • Clarify Route, RouteLocator, RouteDefinition, and RouteDefinitionLocator. The only objects actually exposed to the runtime are CachingRouteLocator and Route.
  • The RouteRefreshListener receives the container event, publishes RefreshRoutesEvent, and triggers the route to be loaded into the CachingRouteLocator.