1.概述
Gateway 创建 Route 对象时,使用 RoutePredicateFactory 创建 Predicate 对象。Predicate 对象可以赋值给 Route.predicate
属性,用于匹配请求对应的 Route 。
在Gateway(二)Route一文中构建Route的时候,通过List<RoutePredicateFactory>
来给 Route.predicate
属性赋值。
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach(factory -> {
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.");
}
this.predicates.put(key, factory);
if (logger.isInfoEnabled()) {
logger.info("Loaded RoutePredicateFactory [" + key + "]");
}
});
}
2.RoutePredicateFactory
RoutePredicateFactory
是路由谓语工厂接口,代码如下:
@FunctionalInterface
public interface RoutePredicateFactory extends ArgumentHints {
String PATTERN_KEY = "pattern";
Predicate<ServerWebExchange> apply(Tuple args);
default String name() {
return NameUtils.normalizePredicateName(getClass());
}
}
#name()
默认方法,调用NameUtils#normalizePredicateName(Class)
方法,获得 RoutePredicateFactory 的名字。该方法截取类名前半段,例如 QueryRoutePredicateFactory 的结果为Query
。#apply()
接口方法,创建 Predicate 。
RoutePredicateFactory 的实现类:
2.1 AfterRoutePredicateFactory
-
Route 匹配 :请求时间满足在配置时间之后。
-
配置:
spring: cloud: gateway: routes: - id: after_route uri: http://example.org predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]
-
代码:
public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange serverWebExchange) { final ZonedDateTime now = ZonedDateTime.now(); return now.isAfter(config.getDatetime()); } @Override public String toString() { return String.format("After: %s", config.getDatetime()); } }; }
2.2 BeforeRoutePredicateFactory
-
Route 匹配 :请求时间满足在配置时间之前。
-
配置:
spring: cloud: gateway: routes: - id: before_route uri: http://example.org predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
-
代码
public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange serverWebExchange) { final ZonedDateTime now = ZonedDateTime.now(); return now.isBefore(config.getDatetime()); } @Override public String toString() { return String.format("Before: %s", config.getDatetime()); } }; }
2.3 BetweenRoutePredicateFactory
-
Route 匹配 :请求时间满足在配置时间之间。
-
配置:
spring: cloud: gateway: routes: - id: between_route uri: http://example.org predicates: - Betweeen=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
-
代码
public Predicate<ServerWebExchange> apply(Config config) { Assert.isTrue(config.getDatetime1().isBefore(config.getDatetime2()), config.getDatetime1() + " must be before " + config.getDatetime2()); return new GatewayPredicate() { @Override public boolean test(ServerWebExchange serverWebExchange) { final ZonedDateTime now = ZonedDateTime.now(); return now.isAfter(config.getDatetime1()) && now.isBefore(config.getDatetime2()); } @Override public String toString() { return String.format("Between: %s and %s", config.getDatetime1(), config.getDatetime2()); } }; }
2.4 CookieRoutePredicateFactory
-
Route 匹配 :请求指定 Cookie 正则匹配指定值。
-
配置:
spring: cloud: gateway: routes: - id: cookie_route uri: http://example.org predicates: - Cookie=chocolate, ch.p
-
代码
public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { List<HttpCookie> cookies = exchange.getRequest().getCookies() .get(config.name); if (cookies == null) { return false; } for (HttpCookie cookie : cookies) { if (cookie.getValue().matches(config.regexp)) { return true; } } return false; } @Override public String toString() { return String.format("Cookie: name=%s regexp=%s", config.name, config.regexp); } }; }
2.5 HeaderRoutePredicateFactory
-
Route 匹配 :请求头满足匹配
-
配置:
spring: cloud: gateway: routes: - id: header_route uri: http://example.org predicates: - Header=X-Request-Id, \d+
-
代码
public Predicate<ServerWebExchange> apply(Config config) { boolean hasRegex = !StringUtils.isEmpty(config.regexp); return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { List<String> values = exchange.getRequest().getHeaders() .getOrDefault(config.header, Collections.emptyList()); if (values.isEmpty()) { return false; } // values is now guaranteed to not be empty if (hasRegex) { // check if a header value matches return values.stream() .anyMatch(value -> value.matches(config.regexp)); } // there is a value and since regexp is empty, we only check existence. return true; } @Override public String toString() { return String.format("Header: %s regexp=%s", config.header, config.regexp); } }; }
2.6 HostRoutePredicateFactory
-
Route 匹配 :请求 Host 匹配指定值。
-
配置:
spring: cloud: gateway: routes: - id: host_route uri: http://example.org predicates: - Host=**.somehost.org
-
代码
public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { String host = exchange.getRequest().getHeaders().getFirst("Host"); Optional<String> optionalPattern = config.getPatterns().stream() .filter(pattern -> pathMatcher.match(pattern, host)).findFirst(); if (optionalPattern.isPresent()) { Map<String, String> variables = pathMatcher .extractUriTemplateVariables(optionalPattern.get(), host); ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables); return true; } return false; } @Override public String toString() { return String.format("Hosts: %s", config.getPatterns()); } }; }
2.7 MethodRoutePredicateFactory
-
Route 匹配 :请求 Method 匹配指定值。
-
配置:
spring: cloud: gateway: routes: - id: method_route uri: http://example.org predicates: - Method=GET
-
代码
public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { HttpMethod requestMethod = exchange.getRequest().getMethod(); return stream(config.getMethods()) .anyMatch(httpMethod -> httpMethod == requestMethod); } @Override public String toString() { return String.format("Methods: %s", Arrays.toString(config.getMethods())); } }; }
2.8 PathRoutePredicateFactory
-
Route 匹配 :请求 Path 匹配指定值。
-
配置:
spring: cloud: gateway: routes: - id: host_route uri: http://example.org predicates: - Path=/foo/{segment}
-
代码
public Predicate<ServerWebExchange> apply(Config config) { final ArrayList<PathPattern> pathPatterns = new ArrayList<>(); synchronized (this.pathPatternParser) { pathPatternParser.setMatchOptionalTrailingSeparator( config.isMatchOptionalTrailingSeparator()); config.getPatterns().forEach(pattern -> { PathPattern pathPattern = this.pathPatternParser.parse(pattern); pathPatterns.add(pathPattern); }); } return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { PathContainer path = parsePath( exchange.getRequest().getURI().getRawPath()); Optional<PathPattern> optionalPathPattern = pathPatterns.stream() .filter(pattern -> pattern.matches(path)).findFirst(); if (optionalPathPattern.isPresent()) { PathPattern pathPattern = optionalPathPattern.get(); traceMatch("Pattern", pathPattern.getPatternString(), path, true); PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path); putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables()); return true; } else { traceMatch("Pattern", config.getPatterns(), path, false); return false; } } @Override public String toString() { return String.format("Paths: %s, match trailing slash: %b", config.getPatterns(), config.isMatchOptionalTrailingSeparator()); } }; }
2.9 QueryRoutePredicateFactory
-
Route 匹配 :请求 QueryParam 匹配指定值。
-
配置:
spring: cloud: gateway: routes: - id: query_route uri: http://example.org predicates: - Query=baz - Query=foo, ba.
-
代码
public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { if (!StringUtils.hasText(config.regexp)) { // check existence of header return exchange.getRequest().getQueryParams() .containsKey(config.param); } List<String> values = exchange.getRequest().getQueryParams() .get(config.param); if (values == null) { return false; } for (String value : values) { if (value != null && value.matches(config.regexp)) { return true; } } return false; } @Override public String toString() { return String.format("Query: param=%s regexp=%s", config.getParam(), config.getRegexp()); } }; }
2.10 RemoteAddrRoutePredicateFactory
-
Route 匹配 :请求来源 IP 在指定范围内。
-
配置:
spring: cloud: gateway: routes: - id: remoteaddr_route uri: http://example.org predicates: - RemoteAddr=192.168.1.1/24
-
代码
public Predicate<ServerWebExchange> apply(Config config) { List<IpSubnetFilterRule> sources = convert(config.sources); return new GatewayPredicate() { @Override public boolean test(ServerWebExchange exchange) { InetSocketAddress remoteAddress = config.remoteAddressResolver .resolve(exchange); if (remoteAddress != null && remoteAddress.getAddress() != null) { String hostAddress = remoteAddress.getAddress().getHostAddress(); String host = exchange.getRequest().getURI().getHost(); if (log.isDebugEnabled() && !hostAddress.equals(host)) { log.debug("Remote addresses didn't match " + hostAddress + " != " + host); } for (IpSubnetFilterRule source : sources) { if (source.matches(remoteAddress)) { return true; } } } return false; } @Override public String toString() { return String.format("RemoteAddrs: %s", config.getSources()); } }; }
3.RoutePredicateHandlerMapping
在上面介绍了所有的Predicate 匹配规则,那么在 Gateway(一)初始化 文章中的工作机制部分讲过,客户端发送请求过来,通过 HandlerMapping 进行predicate 的匹配,匹配成功在进行下面的处理。
-
1. org.springframework.web.reactive.DispatcherHandler
:接收到请求,匹配 HandlerMapping ,此处会匹配到 RoutePredicateHandlerMapping 。 由于Gateway是构建在 reactive 上的,所以这边的web类型就是reactive。public class DispatcherHandler implements WebHandler, ApplicationContextAware { @Override public Mono<Void> handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return createNotFoundError(); } //① 顺序使用 handlerMappings 获得对应的 WebHandler return Flux.fromIterable(this.handlerMappings) //② 获得 Handler .concatMap(mapping -> mapping.getHandler(exchange)) .next() //③ 如果匹配不到 WebHandler ,返回 HANDLER_NOT_FOUND_EXCEPTION 。 .switchIfEmpty(createNotFoundError()) //④ 调用 invokeHandler() 方法,执行 Handler 。 .flatMap(handler -> invokeHandler(exchange, handler)) //⑤ 调用 #handleResult() 方法,处理结果 .flatMap(result -> handleResult(exchange, result)); } }
-
①
顺序使用handlerMappings
获得对应的 WebHandler可以看到有
RoutePredicateHandlerMapping
-
②
获得 Handler -
③
如果匹配不到 WebHandler ,返回HANDLER_NOT_FOUND_EXCEPTION
。 -
④
调用invokeHandler()
方法,执行 Handler 。private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) { if (this.handlerAdapters != null) { //4.1 获取Adapters, WebHandler 的处理器适配器。 for (HandlerAdapter handlerAdapter : this.handlerAdapters) { //4.2 调用support方法 ,是否支持 WebHandler if (handlerAdapter.supports(handler)) { //4.3 调用handle 方法,执行处理器 return handlerAdapter.handle(exchange, handler); } } } return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); }
-
4.1 this.handlerAdapters
:获取Adapters, WebHandler 的处理器适配器。 -
4.2 supports
:调用support方法 ,是否支持 WebHandler//org.springframework.web.reactive.result.SimpleHandlerAdapter @Override public boolean supports(Object handler) { return WebHandler.class.isAssignableFrom(handler.getClass()); }
-
4.3 handle
: 调用handle 方法,执行处理器//org.springframework.web.reactive.result.SimpleHandlerAdapter public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) { WebHandler webHandler = (WebHandler) handler; //4.3.1 执行处理器。例如,WebHandler 为 FilteringWebHandler 时,获得 Route 的 GatewayFilter 数组,创建 GatewayFilterChain 处理请求。 Mono<Void> mono = webHandler.handle(exchange); //4.3.2 在 WebHandler 执行完后 ( #then(Mongo) ),然后返回 Mono.empty() 。 return mono.then(Mono.empty()); }
4.3.1 执行处理器
。例如,WebHandler 为 FilteringWebHandler 时,获得 Route 的 GatewayFilter 数组,创建 GatewayFilterChain 处理请求。4.3.2
在 WebHandler 执行完后 ( #then(Mongo) ),然后返回 Mono.empty() 。
-
-
⑤
调用#handleResult()
方法,处理结果。SimpleHandlerAdapter 返回的是
Mono.empty()
,所以不会触发该方法。private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { return getResultHandler(result).handleResult(exchange, result) .onErrorResume(ex -> result.applyExceptionHandler(ex).flatMap(exceptionResult -> getResultHandler(exceptionResult).handleResult(exchange, exceptionResult))); }
-
-
2. org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
:接收到请求,匹配 Route ,并返回处理 Route 的 FilteringWebHandler 。public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { private final FilteringWebHandler webHandler; private final RouteLocator routeLocator; public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator) { this.webHandler = webHandler; this.routeLocator = routeLocator; setOrder(1); // RequestMappingHandlerMapping 之后 } @Override protected Mono<?> getHandlerInternal(ServerWebExchange exchange) { // 设置 GATEWAY_HANDLER_MAPPER_ATTR 为 RoutePredicateHandlerMapping exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); return lookupRoute(exchange) //2.1 匹配路由 // .log("route-predicate-handler-mapping", Level.FINER) //name this .flatMap((Function<Route, Mono<?>>) r -> { exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isDebugEnabled()) { logger.debug( "Mapping [" + getExchangeDesc(exchange) + "] to " + r); } // 设置 GATEWAY_ROUTE_ATTR 为 匹配的 Route exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); return Mono.just(webHandler); }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {//匹配不到返回 exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isTraceEnabled()) { logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]"); } }))); } }
-
2.1 lookupRoute
匹配路由protected Mono<Route> lookupRoute(ServerWebExchange exchange) { //2.1.1 获取所有路由 return this.routeLocator.getRoutes() .concatMap(route -> Mono.just(route).filterWhen(r -> { // add the current route we are testing exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); //2.1.2并调用 Predicate#apply(ServerWebExchange) 方法,顺序匹配一个 Route。 return r.getPredicate().apply(exchange); }) // instead of immediately stopping main flux due to error, log and // swallow it //2.1.3 未来会增加匹配过程中发生异常的处理。目前,任何一个 Predicate#test(ServerWebExchange) 的方法调用发生异常时,都会导致匹配不到 Route 。一定要注意。 .doOnError(e -> logger.error( "Error applying predicate for route: " + route.getId(), e)) .onErrorResume(e -> Mono.empty())) // .defaultIfEmpty() put a static Route not found // or .switchIfEmpty() // .switchIfEmpty(Mono.<Route>empty().log("noroute")) .next() // TODO: error handling .map(route -> { if (logger.isDebugEnabled()) { logger.debug("Route matched: " + route.getId()); } validateRoute(route, exchange); return route; }); }
2.1.1 getRoutes()
: 获取所有Route,在Gateway(二)Route一文中已经分析过了。2.1.2 Predicate#apply(ServerWebExchange) 方法
,顺序匹配一个 Route。2.1.3:错误处理
,如果在匹配Route 中发现错误,会导致一个Route都匹配不到
-
-
3. org.springframework.cloud.gateway.handler.FilteringWebHandler
:获得 Route 的 GatewayFilter 数组,创建 GatewayFilterChain 处理请求。在Gateway(四)Filter文中分析
参考自:Spring-Cloud-Gateway 源码解析 —— 处理器 (3.1) 之 RoutePredicateFactory 路由谓语工厂