diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index c1906e3f..956279d1 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -272,8 +272,8 @@ public class GatewayAutoConfiguration { @Bean @ConditionalOnMissingBean - public FilteringWebHandler filteringWebHandler(List globalFilters) { - return new FilteringWebHandler(globalFilters); + public FilteringWebHandler filteringWebHandler(List globalFilters, GatewayProperties properties) { + return new FilteringWebHandler(globalFilters, properties.isRouteFilterCacheEnabled()); } @Bean diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java index 53ea714c..356185f5 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java @@ -68,6 +68,19 @@ public class GatewayProperties { */ private boolean failOnRouteDefinitionError = true; + /** + * Enables the route filter cache, defaults to false. + */ + private boolean routeFilterCacheEnabled = false; + + public boolean isRouteFilterCacheEnabled() { + return routeFilterCacheEnabled; + } + + public void setRouteFilterCacheEnabled(boolean routeFilterCacheEnabled) { + this.routeFilterCacheEnabled = routeFilterCacheEnabled; + } + public List getRoutes() { return routes; } @@ -109,6 +122,7 @@ public class GatewayProperties { .append("defaultFilters", defaultFilters) .append("streamingMediaTypes", streamingMediaTypes) .append("failOnRouteDefinitionError", failOnRouteDefinitionError) + .append("routeFilterCacheEnabled", routeFilterCacheEnabled) .toString(); } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java index 395ee027..9f3beb37 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java @@ -18,18 +18,21 @@ package org.springframework.cloud.gateway.handler; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Mono; +import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory; import org.springframework.cloud.gateway.route.Route; +import org.springframework.context.ApplicationListener; import org.springframework.core.DecoratingProxy; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; @@ -49,14 +52,28 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G * @author Yuxin Wang * @since 0.1 */ -public class FilteringWebHandler implements WebHandler { +public class FilteringWebHandler implements WebHandler, ApplicationListener { protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class); private final List globalFilters; + private final ConcurrentHashMap> routeFilterMap = new ConcurrentHashMap(); + + private final boolean routeFilterCacheEnabled; + + @Deprecated public FilteringWebHandler(List globalFilters) { + this(globalFilters, false); + } + + public FilteringWebHandler(List globalFilters, boolean routeFilterCacheEnabled) { this.globalFilters = loadFilters(globalFilters); + this.routeFilterCacheEnabled = routeFilterCacheEnabled; + } + + /* for testing */ ConcurrentHashMap> getRouteFilterMap() { + return routeFilterMap; } private static List loadFilters(List filters) { @@ -76,20 +93,17 @@ public class FilteringWebHandler implements WebHandler { }).collect(Collectors.toList()); } - /* - * TODO: relocate @EventListener(RefreshRoutesEvent.class) void handleRefresh() { - * this.combinedFiltersForRoute.clear(); - */ + @Override + public void onApplicationEvent(RefreshRoutesEvent event) { + if (this.routeFilterCacheEnabled) { + routeFilterMap.clear(); + } + } @Override public Mono handle(ServerWebExchange exchange) { Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); - List gatewayFilters = route.getFilters(); - - List combined = new ArrayList<>(this.globalFilters); - combined.addAll(gatewayFilters); - // TODO: needed or cached? - AnnotationAwareOrderComparator.sort(combined); + List combined = getCombinedFilters(route); if (logger.isDebugEnabled()) { logger.debug("Sorted gatewayFilterFactories: " + combined); @@ -98,6 +112,23 @@ public class FilteringWebHandler implements WebHandler { return new DefaultGatewayFilterChain(combined).filter(exchange); } + protected List getCombinedFilters(Route route) { + if (this.routeFilterCacheEnabled) { + return routeFilterMap.computeIfAbsent(route, this::getAllFilters); + } + else { + return getAllFilters(route); + } + } + + protected List getAllFilters(Route route) { + List gatewayFilters = route.getFilters(); + List combined = new ArrayList<>(this.globalFilters); + combined.addAll(gatewayFilters); + AnnotationAwareOrderComparator.sort(combined); + return combined; + } + private static class DefaultGatewayFilterChain implements GatewayFilterChain { private final int index; diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java index ac2309fc..c67d61b9 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/RouteRefreshListener.java @@ -46,7 +46,12 @@ public class RouteRefreshListener implements ApplicationListener r.path("/get").filters(f -> f.prefixPath("/httpbin")).uri("lb://testservice")) + .route("testRoute1", + r -> r.path("/anything/testRoute1") + .filters(f -> f.prefixPath("/httpbin")) + .uri("lb://testservice")) + .build(); + } + + } + +}