一、前言
我们上一篇 Spring Cloud Gateway 源码剖析之Route数据模型 中讲到了 Route 数据模型,其中有 Predicate 属性。
这一篇我们就来讲一讲 Predicate 谓词相关源码。Predicate 对象是由 RoutePredicateFactory 工厂类创建,那我们就来看下 RoutePredicateFactory 是如何创建 Predicate 的。
二、RoutePredicateFactory
从上图可知
- #name() 默认方法,调用 NameUtils#normalizePredicateName(Class) 方法,获得 RoutePredicateFactory 的名字。该方法截取类名前半段,例如 QueryRoutePredicateFactory 的结果为 Query 。
- #apply() 接口方法,创建 Predicate 。
可以直接看到处理器类与相关谓词工厂类如下
这里再对相关谓词工厂进行分类:
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(AfterRoutePredicateFactory.Config config) { ZonedDateTime datetime = config.getDatetime(); return (exchange) -> { ZonedDateTime now = ZonedDateTime.now(); return now.isAfter(datetime); }; }
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(BeforeRoutePredicateFactory.Config config) { ZonedDateTime datetime = config.getDatetime(); return (exchange) -> { ZonedDateTime now = ZonedDateTime.now(); return now.isBefore(datetime); }; }
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(BetweenRoutePredicateFactory.Config config) { ZonedDateTime datetime1 = config.datetime1; ZonedDateTime datetime2 = config.datetime2; Assert.isTrue(datetime1.isBefore(datetime2), config.datetime1 + " must be before " + config.datetime2); return (exchange) -> { ZonedDateTime now = ZonedDateTime.now(); return now.isAfter(datetime1) && now.isBefore(datetime2); }; }
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(CookieRoutePredicateFactory.Config config) { return (exchange) -> { List<HttpCookie> cookies = (List)exchange.getRequest().getCookies().get(config.name); if (cookies == null) { return false; } else { Iterator var3 = cookies.iterator(); HttpCookie cookie; do { if (!var3.hasNext()) { return false; } cookie = (HttpCookie)var3.next(); } while(!cookie.getValue().matches(config.regexp)); return true; } }; }
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(HeaderRoutePredicateFactory.Config config) { boolean hasRegex = !StringUtils.isEmpty(config.regexp); return (exchange) -> { List<String> values = (List)exchange.getRequest().getHeaders().getOrDefault(config.header, Collections.emptyList()); if (values.isEmpty()) { return false; } else { return hasRegex ? values.stream().anyMatch((value) -> { return value.matches(config.regexp); }) : true; } }; }
2.6 HostRoutePredicateFactory
- Route 匹配 :请求 Host 匹配指定值
- 配置:
spring: cloud: gateway: routes: - id: host_route uri: http://example.org predicates: - Host=**.somehost.org
- 代码:
public Predicate<ServerWebExchange> apply(HostRoutePredicateFactory.Config config) { return (exchange) -> { String host = exchange.getRequest().getHeaders().getFirst("Host"); Optional<String> optionalPattern = config.getPatterns().stream().filter((pattern) -> { return this.pathMatcher.match(pattern, host); }).findFirst(); if (optionalPattern.isPresent()) { Map<String, String> variables = this.pathMatcher.extractUriTemplateVariables((String)optionalPattern.get(), host); ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables); return true; } else { return false; } }; }
2.7 MethodRoutePredicateFactory
- Route 匹配 :请求 Method 匹配指定值
- 配置:
spring: cloud: gateway: routes: - id: method_route uri: http://example.org predicates: - Method=GET
- 代码:
public Predicate<ServerWebExchange> apply(MethodRoutePredicateFactory.Config config) { return (exchange) -> { HttpMethod requestMethod = exchange.getRequest().getMethod(); return requestMethod == config.getMethod(); }; }
2.8 PathRoutePredicateFactory
- Route 匹配 :请求 Path 匹配指定值
- 配置:
spring: cloud: gateway: routes: - id: host_route uri: http://example.org predicates: - Path=/foo/{segment}
- 代码:
public Predicate<ServerWebExchange> apply(PathRoutePredicateFactory.Config config) { ArrayList<PathPattern> pathPatterns = new ArrayList(); synchronized(this.pathPatternParser) { this.pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator()); config.getPatterns().forEach((pattern) -> { PathPattern pathPattern = this.pathPatternParser.parse(pattern); pathPatterns.add(pathPattern); }); } return (exchange) -> { PathContainer path = PathContainer.parsePath(exchange.getRequest().getURI().getPath()); Optional<PathPattern> optionalPathPattern = pathPatterns.stream().filter((pattern) -> { return pattern.matches(path); }).findFirst(); if (optionalPathPattern.isPresent()) { PathPattern pathPattern = (PathPattern)optionalPathPattern.get(); traceMatch("Pattern", pathPattern.getPatternString(), path, true); PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path); ServerWebExchangeUtils.putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables()); return true; } else { traceMatch("Pattern", config.getPatterns(), path, false); return false; } }; }
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(QueryRoutePredicateFactory.Config config) { return (exchange) -> { if (!StringUtils.hasText(config.regexp)) { return exchange.getRequest().getQueryParams().containsKey(config.param); } else { List<String> values = (List)exchange.getRequest().getQueryParams().get(config.param); if (values == null) { return false; } else { Iterator var3 = values.iterator(); String value; do { if (!var3.hasNext()) { return false; } value = (String)var3.next(); } while(value == null || !value.matches(config.regexp)); return true; } } }; }
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(RemoteAddrRoutePredicateFactory.Config config) { List<IpSubnetFilterRule> sources = this.convert(config.sources); return (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); } Iterator var6 = sources.iterator(); while(var6.hasNext()) { IpSubnetFilterRule source = (IpSubnetFilterRule)var6.next(); if (source.matches(remoteAddress)) { return true; } } } return false; }; }
三、RoutePredicateHandlerMapping
我们先来看下 Spring Cloud Gateway 官网提供的架构图:
上一节讲完了常见分类的 Predicate 匹配规则,客户端发送请求过来,通过 HandlerMapping 进行 predicate 的匹配,匹配成功再进行下面的处理。
3.1 org.springframework.web.reactive.DispatcherHandler
接收到请求,匹配 HandlerMapping ,此处会匹配到 RoutePredicateHandlerMapping。由于 Gateway 是构建在 reactive 上的,所以这边的 web 类型就是 reactive。
public class DispatcherHandler implements WebHandler, ApplicationContextAware { private static final Exception HANDLER_NOT_FOUND_EXCEPTION; @Nullable private List<HandlerMapping> handlerMappings; @Nullable private List<HandlerAdapter> handlerAdapters; @Nullable private List<HandlerResultHandler> resultHandlers; public Mono<Void> handle(ServerWebExchange exchange) { return this.handlerMappings == null ? this.createNotFoundError() : // 顺序使用 handlerMappings 获得对应的 WebHandler Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { // 获得 Handler return mapping.getHandler(exchange); // 如果匹配不到 WebHandler ,返回 HANDLER_NOT_FOUND_EXCEPTION 。 }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { // 调用 invokeHandler() 方法,执行 Handler 。 return this.invokeHandler(exchange, handler); }).flatMap((result) -> { // 调用 handleResult() 方法,处理结果 return this.handleResult(exchange, result); }); } ... }
继续跟一下 invokeHandler() 方法:
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) { if (this.handlerAdapters != null) { // 获取Adapters, WebHandler 的处理器适配器。 Iterator var3 = this.handlerAdapters.iterator(); while(var3.hasNext()) { HandlerAdapter handlerAdapter = (HandlerAdapter)var3.next(); // 调用support方法 ,是否支持 WebHandler if (handlerAdapter.supports(handler)) { // 调用handle 方法,执行处理器 return handlerAdapter.handle(exchange, handler); } } } return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); }
public boolean supports(Object handler) { return WebHandler.class.isAssignableFrom(handler.getClass()); }
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) { WebHandler webHandler = (WebHandler)handler; // 执行处理器。例如,WebHandler 为 FilteringWebHandler 时,获得 Route 的 GatewayFilter 数组,创建 GatewayFilterChain 处理请求。 Mono<Void> mono = webHandler.handle(exchange); // 在 WebHandler 执行完后 #then(Mongo),然后返回 Mono.empty() 。 return mono.then(Mono.empty()); }
SimpleHandlerAdapter 返回的是 Mono.empty() ,所以不会触发该方法。
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { return this.getResultHandler(result).handleResult(exchange, result).onErrorResume((ex) -> { return result.applyExceptionHandler(ex).flatMap((exceptionResult) -> { return this.getResultHandler(exceptionResult).handleResult(exchange, exceptionResult); }); }); }
3.2 org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
接收到请求,匹配 Route ,并返回处理 Route 的 FilteringWebHandler。
SimpleHandlerAdapter#handle(ServerWebExchange, Object) 调用 FilteringWebHandler#handle(ServerWebExchange) 方法,处理请求。
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { private final FilteringWebHandler webHandler; private final RouteLocator routeLocator; private final Integer managmentPort; public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { this.webHandler = webHandler; this.routeLocator = routeLocator; if (environment.containsProperty("management.server.port")) { this.managmentPort = new Integer(environment.getProperty("management.server.port")); } else { this.managmentPort = null; } // RequestMappingHandlerMapping 之后 this.setOrder(1); this.setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); } protected Mono<?> getHandlerInternal(ServerWebExchange exchange) { if (this.managmentPort != null && exchange.getRequest().getURI().getPort() == this.managmentPort) { return Mono.empty(); } else { // 设置 GATEWAY_HANDLER_MAPPER_ATTR 为 RoutePredicateHandlerMappingexchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getSimpleName()); // 匹配路由 return this.lookupRoute(exchange).flatMap((r) -> { exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR); if (this.logger.isDebugEnabled()) { this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r); } // 设置 GATEWAY_ROUTE_ATTR 为 匹配的 Route exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r); return Mono.just(this.webHandler); }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> { //匹配不到返回 exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR); if (this.logger.isTraceEnabled()) { this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]"); } }))); } } }
跟一下 lookupRoute 匹配路由,这个方法是网关的核心,像我们自研的网关,如果你刚接手公司中的网关项目,找到匹配路由再展开,能帮你省很多时间,快速熟悉公司中网关的项目。
protected Mono<Route> lookupRoute(ServerWebExchange exchange) { // 获取所有路由 return this.routeLocator.getRoutes().concatMap((route) -> { return Mono.just(route).filterWhen((r) -> { exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); // 并调用 Predicate#apply(ServerWebExchange) 方法,顺序匹配一个 Route。 return (Publisher)r.getPredicate().apply(exchange); // 未来会增加匹配过程中发生异常的处理。目前,任何一个 Predicate#test(ServerWebExchange) 的方法调用发生异常时,都会导致匹配不到 Route。一定要注意。 }).doOnError((e) -> { this.logger.error("Error applying predicate for route: " + route.getId(), e); }).onErrorResume((e) -> { return Mono.empty(); }); }).next().map((route) -> { if (this.logger.isDebugEnabled()) { this.logger.debug("Route matched: " + route.getId()); } this.validateRoute(route, exchange); return route; }); }
3.3 org.springframework.cloud.gateway.handler.FilteringWebHandler
获得 Route 的 GatewayFilter 数组,创建 GatewayFilterChain 处理请求。这里我们放到下一篇来讲,下一篇也很重要,从原理来说也不是很难理解,就是一个过滤器链。但从 Gateway 的两大核心:路由+过滤链来说,这又很重要。
欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。
喜欢的话,点赞、再看、分享三连。