Elvis Wang mail@wangbo.im
2019-08-21T09:23:36+08:00
Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Reactor is a fourth-generation Reactive library for building non-blocking applications on the JVM based on the Reactive Streams Specification.
Mono<T>
Flux<T>
Schedular
First
or
Then
or
Finally
First
Flux<User> flux = Flux.create(sink -> {
final User[] users = userDao.findByName(name);
if (users != null) {
for (User user : users) {
sink.next(user);
}
sink.complete();
} else {
sink.error(
new IllegalStateException(
"Invalid name: " + name));
}
});
or
Then
or
Finally
Spring WebFlux - Web on Reactive Stack
fully non-blocking, supports Reactive Streams back pressure, and runs on such servers as Netty, Undertow, and Servlet 3.1+ containers.
- the Spring WebFlux framework
- the reactive WebClient
- support for testing
- and reactive libraries.
org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory#getWebServer
org.springframework.boot.web.embedded.netty.NettyWebServer#start
org.springframework.http.server.reactive.ReactorHttpHandlerAdapter#apply
org.springframework.http.server.reactive.HttpHandler#handle
@Configuration
@ConditionalOnClass({ DispatcherHandler.class, HttpHandler.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnMissingBean(HttpHandler.class)
@AutoConfigureAfter({ WebFluxAutoConfiguration.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class HttpHandlerAutoConfiguration {
@Configuration
public static class AnnotationConfig {
private ApplicationContext applicationContext;
public AnnotationConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public HttpHandler httpHandler() {
return WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
}
}
}
public static final String WEB_HANDLER_BEAN_NAME = "webHandler";
private WebHttpHandlerBuilder(WebHandler webHandler, @Nullable ApplicationContext applicationContext) {
this.webHandler = webHandler; // HERE
this.applicationContext = applicationContext;
}
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(
context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context); // HERE
// ...
return builder;
}
public HttpHandler build() {
WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters); // HERE
decorated = new ExceptionHandlingWebHandler(decorated, this.exceptionHandlers); // HERE
HttpWebHandlerAdapter adapted = new HttpWebHandlerAdapter(decorated); //HERE
// ...
return adapted;
}
org.springframework.web.reactive.config.WebFluxConfigurationSupport
org.springframework.web.reactive.DispatcherHandler
protected void initStrategies(ApplicationContext context) {
Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
AnnotationAwareOrderComparator.sort(mappings);
this.handlerMappings = Collections.unmodifiableList(mappings);
}
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
return Flux.fromIterable(this.handlerMappings)
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
org.springframework.cloud.gateway.config.GatewayAutoConfiguration
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class,
WebFluxAutoConfiguration.class })
@AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class,
GatewayClassPathWarningAutoConfiguration.class })
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(
FilteringWebHandler webHandler, RouteLocator routeLocator,
GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator,
globalCorsProperties, environment);
}
@Bean
@Primary
// TODO: property to disable composite?
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters);
}
}
org.springframework.cloud.gateway.route.RouteLocator
org.springframework.cloud.gateway.handler.FilteringWebHandler
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
// individually filter routes so that filterWhen error delaying is not a
// problem
.concatMap(route -> Mono.just(route).filterWhen(r -> {
// add the current route we are testing
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return r.getPredicate().apply(exchange);
})
// ...
public Mono<Void> handle(ServerWebExchange exchange) {
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
List<GatewayFilter> gatewayFilters = route.getFilters();
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < filters.size()) {
GatewayFilter filter = filters.get(this.index);
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
this.index + 1);
return filter.filter(exchange, chain);
}
else {
return Mono.empty(); // complete
}
});
}
org.springframework.cloud.gateway.route.CachingRouteLocator
org.springframework.cloud.gateway.route.CompositeRouteLocator
All org.springframework.cloud.gateway.route.RouteLocator beans will be collected automatically.
org.springframework.cloud.gateway.route.Route
org.springframework.cloud.gateway.route.RouteLocator
org.springframework.cloud.gateway.route.CachingRouteLocator
org.springframework.cloud.gateway.route.CompositeRouteLocator
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator
org.springframework.cloud.gateway.route.RouteDefinitionLocator
package org.springframework.cloud.gateway.route;
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator
org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator
org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository
org.springframework.cloud.gateway.config.GatewayAutoConfiguration
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(
List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(
Flux.fromIterable(routeDefinitionLocators));
}
All org.springframework.cloud.gateway.route.RouteDefinitionLocator beans will be collected automatically.
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#lookUp
org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory<C>
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
AsyncPredicate<ServerWebExchange> applyAsync(Consumer<C> consumer);
Class<C> getConfigClass();
}
Reflection
org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory
org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#loadGatewayFilters
org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory
org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory<T>
public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
default GatewayFilter apply(Consumer<C> consumer) {
C config = newConfig();
consumer.accept(config);
return apply(config);
}
default Class<C> getConfigClass() {
throw new UnsupportedOperationException("getConfigClass() not implemented");
}
}
org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory
org.springframework.cloud.gateway.filter.ratelimit.KeyResolver
org.springframework.cloud.gateway.filter.ratelimit.RateLimiter<T>
public interface RateLimiter<C> extends StatefulConfigurable<C> {
Mono<Response> isAllowed(String routeId, String id);
}
org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter
org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
For uris like “lb://virtual_host/path”
org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
com.netflix.loadbalancer.ILoadBalancer
org.springframework.cloud.netflix.ribbon.SpringClientFactory
com.netflix.loadbalancer.IRule
com.netflix.loadbalancer.RandomRule
com.netflix.loadbalancer.RetryRule
com.netflix.loadbalancer.RoundRobinRule
com.netflix.loadbalancer.WeightedResponseTimeRule
com.netflix.loadbalancer.IPing
com.netflix.loadbalancer.PingUrl
com.netflix.loadbalancer.NoOpPing
com.netflix.loadbalancer.PingConstant
END