Adds CachingRouteReader

This allows an atomic refresh of routes.
This commit is contained in:
Spencer Gibb
2017-01-27 18:38:53 -07:00
parent d0a7c39e9d
commit 98b1cd574e
5 changed files with 67 additions and 21 deletions

View File

@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.cloud.gateway.api.CachingRouteReader;
import org.springframework.cloud.gateway.api.RouteReader;
import org.springframework.cloud.gateway.config.Route;
import org.springframework.cloud.gateway.filter.GlobalFilter;
@@ -13,9 +14,11 @@ import org.springframework.cloud.gateway.handler.FilteringWebHandler;
import org.springframework.core.Ordered;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
@@ -43,9 +46,17 @@ public class GatewayEndpoint {/*extends AbstractEndpoint<Map<String, Object>> {*
/*@Override
public Map<String, Object> invoke() {
}*/
//TODO: this should really be a listener that responds to a RefreshEvent
@PostMapping("/refresh")
public Flux<Route> refresh() {
if (this.routeReader instanceof CachingRouteReader) {
return ((CachingRouteReader)this.routeReader).refresh();
}
return Flux.empty();
}
@GetMapping("/globalfilters")
public Map<String, Object> globalfilters() {
return getNamesToOrders(this.globalFilters);

View File

@@ -0,0 +1,40 @@
package org.springframework.cloud.gateway.api;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.cloud.gateway.config.Route;
import reactor.core.publisher.Flux;
/**
* @author Spencer Gibb
*/
public class CachingRouteReader implements RouteReader {
private final RouteReader delegate;
private final AtomicReference<List<Route>> cachedRoutes = new AtomicReference<>();
public CachingRouteReader(RouteReader delegate) {
this.delegate = delegate;
this.cachedRoutes.compareAndSet(null, collectRoutes());
}
@Override
public Flux<Route> getRoutes() {
return Flux.fromIterable(this.cachedRoutes.get());
}
/**
* Sets the new routes
* @return old routes
*/
public Flux<Route> refresh() {
return Flux.fromIterable(this.cachedRoutes.getAndUpdate(
routes -> CachingRouteReader.this.collectRoutes()));
}
private List<Route> collectRoutes() {
return this.delegate.getRoutes().collectList().block();
}
}

View File

@@ -10,6 +10,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
import org.springframework.cloud.gateway.api.CachingRouteReader;
import org.springframework.cloud.gateway.api.RouteReader;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
@@ -67,8 +68,9 @@ public class GatewayAutoConfiguration {
@Bean
@ConditionalOnMissingBean(RouteReader.class)
public PropertiesRouteReader propertiesRouteReader(GatewayProperties properties) {
return new PropertiesRouteReader(properties);
public RouteReader propertiesRouteReader(GatewayProperties properties) {
//TODO: how to automatically apply CachingRouteReader
return new CachingRouteReader(new PropertiesRouteReader(properties));
}
@Bean

View File

@@ -31,8 +31,6 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private RouteReader routeReader;
private WebHandler webHandler;
private List<Route> routes;
public RoutePredicateHandlerMapping(WebHandler webHandler, Map<String, RoutePredicate> predicates,
RouteReader routeReader) {
this.webHandler = webHandler;
@@ -58,24 +56,13 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
return name.replace(RoutePredicate.class.getSimpleName(), "");
}
@Override
protected void initApplicationContext() throws BeansException {
super.initApplicationContext();
Flux<Route> routes = this.routeReader.getRoutes();
registerHandlers(routes.collectList().block()); //TODO: convert rest of class to Reactive
}
protected void registerHandlers(List<Route> routes) {
this.routes = routes;
}
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getClass().getSimpleName());
Route route;
try {
route = lookupRoute(this.routes, exchange);
route = lookupRoute(exchange);
}
catch (Exception ex) {
return Mono.error(ex);
@@ -107,7 +94,9 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
}
protected Route lookupRoute(List<Route> routes, ServerWebExchange exchange) throws Exception {
protected Route lookupRoute(ServerWebExchange exchange) throws Exception {
List<Route> routes = this.routeReader.getRoutes().collectList().block(); //TODO: convert rest of class to Reactive
for (Route route : routes) {
if (!route.getPredicates().isEmpty()) {
//TODO: cache predicate

View File

@@ -5,7 +5,9 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.api.CachingRouteReader;
import org.springframework.cloud.gateway.api.CompositeRouteReader;
import org.springframework.cloud.gateway.api.RouteReader;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.config.PropertiesRouteReader;
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteReader;
@@ -42,9 +44,11 @@ public class GatewayTestApplication {
@Bean
@Primary
public CompositeRouteReader compositeRouteReader(DiscoveryClientRouteReader discoveryClientRouteReader,
PropertiesRouteReader propertiesRouteReader) {
return new CompositeRouteReader(Flux.just(discoveryClientRouteReader, propertiesRouteReader));
public RouteReader compositeRouteReader(DiscoveryClientRouteReader discoveryClientRouteReader,
PropertiesRouteReader propertiesRouteReader) {
final Flux<RouteReader> flux = Flux.just(discoveryClientRouteReader, propertiesRouteReader);
final CompositeRouteReader composite = new CompositeRouteReader(flux);
return new CachingRouteReader(composite);
}
}