In the previous article, we saw that you can configure the following configuration to Filter and forward requests, and that there are multiple filters and Predicate built into the SCG. Through the similar – Path = / login or – StripPrefix = 1 that can match to SCG built-in PathRoutePredicateFactory and StripPrefixGatewayFilterFactory, So how does SCG encapsulate and match our configuration?
GatewayProperties gateway: routes: -id: user-service #//127.0.0.1:8080 # Specifies the destination address to which the route is routed-path =/login,/loginUser filters: -stripprefix =. /loginUser filters: -stripprefix =1# say belowCopy the code
Automatically.
SCG is based on Springboot, which automatically assembs beans needed by reading spring.factories files, The spring.factories file in the spring-Cloud-gateway-server project configures the classes that the SCG needs to auto-assemble.
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayHystrixCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor
Copy the code
This article only needs to focus on** GatewayAutoConfiguration****, the core configuration class of SCG. * * from GatewayAutoConfiguration annotations can see @ ConditionalOnProperty ` ` (name = ` ` spring. Cloud. Gateway. “enabled” ` `, ` ` matchIfMissing = ` ` true ` `), by the spring. Cloud. Gateway. Enabled to configure SCG opened and closed, and the default is open
GatewayAutoConfiguration
The primary component initialized in
GatewayProperties
: The purpose of this class was illustrated in the previous article to read the configuration in the encapsulation configuration fileRouteDefinition
,FilterDefinition
,PredicationDefinition
Is the routing informationRouteDefinitionLocator
: Stores the routing information read from the configuration fileRouteLocator
: beans required by the API driverFilteringWebHandler
: The following article will do the analysisRoutePredicateHandlerMapping
: The following article will do the analysisFilterFactory
: createorg.springframework.cloud.gateway.filter.factory
Package all implementationsGatewayFilterFactory
The class ofPredicateFactory
: createorg.springframework.cloud.gateway.handler.predicate
Package under all implementationRoutePredicateFactory
The class of the interface
GatewayProperties
/** * Read the {@link RouteDefinition} ->>{@link FilterDefinition} ->>{@link PredicateDefinition} and encapsulate * @return */
@Bean
public GatewayProperties gatewayProperties() {
return new GatewayProperties();
}
Copy the code
public class GatewayProperties {
/** * Properties prefix. */
public static final String PREFIX = "spring.cloud.gateway";
/** * List of Routes. */
@NotNull
@Valid
private List<RouteDefinition> routes = new ArrayList<>();
/** * List of filters that apply to each route */
private List<FilterDefinition> defaultFilters = new ArrayList<>();
..........省略部分代码............
}
Copy the code
RouteDefinition Indicates the route information
public class RouteDefinition {
// Route ID. If set to null, SCG will generate a random route ID
private String id;
@NotEmpty
@Valid
// Configure the assertion information
private List<PredicateDefinition> predicates = new ArrayList<>();
@Valid
// Information about the configured filter
private List<FilterDefinition> filters = new ArrayList<>();
@NotNull
// Destination URI to be forwarded
private URI uri;
}
Copy the code
PredicateDefinition Specifies the assertion information
public class PredicateDefinition {
@NotNull
/ * * * the name of the assertion, and {@ link AbstractRoutePredicateFactory} a subclass of the same name prefix * /
private String name;
Key :_genkey_0 value:/login */
private Map<String, String> args = new LinkedHashMap<>();
public PredicateDefinition() {
}
public PredicateDefinition(String text) {
int eqIdx = text.indexOf('=');
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse PredicateDefinition text '"
+ text + "'" + ", must be of the form name=value");
}
setName(text.substring(0, eqIdx));
// Split the string to the right of "=" with a ","
String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");
Key (_genkey_+ parameter subscript) is generated randomly, and value is the parameter
for (int i = 0; i < args.length; i++) { this.args.put(NameUtils.generateName(i), args[i]); }}}Copy the code
FilterDefinition Filter information
This is similar to PredicateDefinition. At this point, you have encapsulated the information from the configuration into the corresponding Definition
RouteDefinitionLocator
public interface RouteDefinitionLocator {
// Get all routing information
Flux<RouteDefinition> getRouteDefinitions();
}
Copy the code
The main implementation class: PropertiesRouteDefinitionLocator, CompositeRouteDefinitionLocator
/ * * * PropertiesRouteDefinitionLocator is {@ link RouteDefinitionLocator} implementation class used to store the read from the configuration file of the routing information * @ param properties GatewayProperties Bean * @return */
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
Copy the code
/** * Assemble the above RouteDefinitionLocator again * @param routeDefinitionLocators * @return */
@Bean
@Primary / / is defined as Primary to below when assembling RouteDefinitionRouteLocator the Bean as the injected RouteDefinitionLocator Beanpublic RouteDefinitionLocator routeDefinitionLocator( List<RouteDefinitionLocator> routeDefinitionLocators) { return new CompositeRouteDefinitionLocator( Flux.fromIterable(routeDefinitionLocators)); }Copy the code
This class determines whether the route ID is empty and generates one if it is
public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return this.delegates
.flatMapSequential(RouteDefinitionLocator::getRouteDefinitions)
.flatMap(routeDefinition -> {
if (routeDefinition.getId() == null) {
// If the route ID is empty, one is generated
return randomId().map(id -> {
routeDefinition.setId(id);
if (log.isDebugEnabled()) {
log.debug(
"Id set on route definition: "+ routeDefinition); } return routeDefinition; }); } return Mono.just(routeDefinition); }); } protected Mono<String> randomId() { return Mono.fromSupplier(idGenerator::toString) .publishOn(Schedulers.boundedElastic()); }}Copy the code
GatewayFilter Factory beans
* * assembly org. Springframework. Cloud. Gateway. Handler. The predicate package under RoutePredicateFactory * * implementation class
@Bean @ConditionalOnEnabledFilter public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() { return new AddRequestHeaderGatewayFilterFactory(); } @Bean @ConditionalOnEnabledFilter public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() { return new MapRequestHeaderGatewayFilterFactory(); }... And so on...Copy the code
Predicate Factory beans
Assembly * * org. Springframework. Cloud. Gateway. Filter. The factory under package * * GatewayFilterFactory implementation class
@Bean @ConditionalOnEnabledPredicate public AfterRoutePredicateFactory afterRoutePredicateFactory() { return new AfterRoutePredicateFactory(); } @Bean @ConditionalOnEnabledPredicate public BeforeRoutePredicateFactory beforeRoutePredicateFactory() { return new BeforeRoutePredicateFactory(); }... And so on...Copy the code
RouteLocator
public interface RouteLocator {
// To get the route
Flux<Route> getRoutes();
}
Copy the code
RouteDefinitionRouteLocator
The other two implementations are the focus of this section and will be explained later
/** ** @param properties is the assembly GatewayProperties Bean * @param gatewayFilters are the assembly GatewayFilterFactory implementation classes * @param Predicates are all implementation classes of the assembled RouteDefinitionFactory. * @param routeDefinitionLocator is the assembled routeDefinitionLocator ->CompositeRouteDefinitionLocator * @return */
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
/ * * * CachingRouteLocator RoutePredicateHandlerMapping RouteLocator, see {@ link this# RoutePredicateHandlerMapping} * @ param Above routeLocators assembly RouteDefinitionRouteLocator Bean * @ return * /
@Bean
@Primary / / is defined as the Primary for the bottom assembly RoutePredicateHandlerMapping
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
Copy the code
RouteDefinitionRouteLocator
This class consists of RouteDefinitionLocator, RoutePredicateFactory, and GatewayFilterFactory, which can be converted to a Route.
public class RouteDefinitionRouteLocator
implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
/** * Default filters name. */
public static final String DEFAULT_FILTERS = "defaultFilters";
protected final Log logger = LogFactory.getLog(getClass());
private final RouteDefinitionLocator routeDefinitionLocator;
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
private final GatewayProperties gatewayProperties;
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties,
ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = configurationService;
// Initializes Predicate information (all of it)
initFactories(predicates);
// Initializes Filter information (all), similar to initializing Predicate Predicate information
gatewayFilterFactories.forEach(
factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach(factory -> {
/ / the key for RoutePredicateFactory implementation class name prefix such as AfterRoutePredicateFactory is key for After
String key = factory.name();
if (this.predicates.containsKey(key)) {
this.logger.warn("A RoutePredicateFactory named " + key
+ " already exists, class: " + this.predicates.get(key)
+ ". It will be overwritten.");
}
// If the assertion Factory already exists, override it, that is, the one built into the SCG
this.predicates.put(key, factory);
if (logger.isInfoEnabled()) {
logger.info("Loaded RoutePredicateFactory [" + key + "]"); }}); }Copy the code
getRoutes()
This method is called by CachingRouteLocator and returns routing information.
Cachin Group Locator will be covered in a later article
@Override
public Flux<Route> getRoutes() {
// Obtain Route by RouteDefinitionsFlux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions() .map(this::convertToRoute); if (! gatewayProperties.isFailOnRouteDefinitionError()) {// instead of letting error bubble up, continue
routes = routes.onErrorContinue((error, obj) -> {
if (logger.isWarnEnabled()) {
logger.warn("RouteDefinition id " + ((RouteDefinition) obj).getId()
+ " will be ignored. Definition has invalid configs, "+ error.getMessage()); }}); } return routes.map(route -> { if (logger.isDebugEnabled()) { logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
Copy the code
ConvertToRoute converts RouteDefinition to Route
RouteDefinition = Route * @param RouteDefinition * @return */
private Route convertToRoute(RouteDefinition routeDefinition) {
/** * key * Gets the assertion of RouteDefinition */
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
/** * key * Get the Filter of RouteDefinition */
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
Copy the code
combinePredicates
Merge PredicateDefinition into one AsyncPredicate
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
if (predicates == null || predicates.isEmpty()) {
// this is a very rare case, but possible, just match all
return AsyncPredicate.from(exchange -> true);
}
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
// This is done to connect multiple AsyncPredicate classes with (and) if the RouteDefinition is configured with multiple Predicate classes
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) {
// Find the RoutePredicateFactory corresponding to the PredicateDefinition
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find RoutePredicateFactory with name "
+ predicate.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + route.getId() + " applying "
+ predicate.getArgs() + " to " + predicate.getName());
}
// Every RoutePredicateFactory implementation has a Config, which can be interpreted as the parameter rules we configure
// @formatter:off
Object config = this.configurationService.with(factory)
.name(predicate.getName())
.properties(predicate.getArgs())
.eventFunction((bound, properties) -> new PredicateArgsEvent(
RouteDefinitionRouteLocator.this, route.getId(), properties))
.bind();
// @formatter:on
// Generate asynchronous assertions
return factory.applyAsync(config);
}
Copy the code
getFilters
Get all filters, sorted
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
// Add a default filterif (! this.gatewayProperties.getDefaultFilters().isEmpty()) { filters.addAll(loadGatewayFilters(DEFAULT_FILTERS, new ArrayList<>(this.gatewayProperties.getDefaultFilters()))); }// Add a configured filterif (! 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(); i++) {
FilterDefinition definition = filterDefinitions.get(i);
// Get the GatewayFilterFactory of FilterDefinition
GatewayFilterFactory factory = this.gatewayFilterFactories
.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find GatewayFilterFactory with name "
+ definition.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter "
+ definition.getArgs() + " to " + definition.getName());
}
// Generates a configuration class, similar to Predicate
// @formatter:off
Object configuration = this.configurationService.with(factory)
.name(definition.getName())
.properties(definition.getArgs())
.eventFunction((bound, properties) -> new FilterArgsEvent(
// TODO: why explicit cast needed or java compile fails
RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
.bind();
// @formatter:on
// some filters require routeId
// TODO: is there a better place to apply this?
if (configuration instanceof HasRouteId) {
HasRouteId hasRouteId = (HasRouteId) configuration;
hasRouteId.setRouteId(id);
}
/ / generated 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
At this point, the main character Route finally appeared. Now that you understand how the SCG generates routes and assertions and filters for routes through configuration, you can analyze the incoming and forwarding of requests.