Spring Cloud Gateway 源碼剖析之Predicate謂詞詳解

點(diǎn)擊上方老周聊架構(gòu)關(guān)注我
一、前言
我們上一篇?Spring Cloud Gateway 源碼剖析之Route數(shù)據(jù)模型?中講到了 Route 數(shù)據(jù)模型,其中有 Predicate 屬性。

這一篇我們就來(lái)講一講 Predicate 謂詞相關(guān)源碼。Predicate 對(duì)象是由 RoutePredicateFactory 工廠類創(chuàng)建,那我們就來(lái)看下 RoutePredicateFactory 是如何創(chuàng)建 Predicate 的。

二、RoutePredicateFactory
從上圖可知
#name() 默認(rèn)方法,調(diào)用 NameUtils#normalizePredicateName(Class) 方法,獲得 RoutePredicateFactory 的名字。該方法截取類名前半段,例如 QueryRoutePredicateFactory 的結(jié)果為 Query 。
#apply() 接口方法,創(chuàng)建 Predicate 。
可以直接看到處理器類與相關(guān)謂詞工廠類如下

這里再對(duì)相關(guān)謂詞工廠進(jìn)行分類:

2.1 AfterRoutePredicateFactory
Route 匹配 :請(qǐng)求時(shí)間滿足在配置時(shí)間之后
配置:
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 匹配 :請(qǐng)求時(shí)間滿足在配置時(shí)間之前
配置:
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 匹配 :請(qǐng)求時(shí)間滿足在配置時(shí)間之間
配置:
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 匹配 :請(qǐng)求指定 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 匹配 :請(qǐng)求頭滿足匹配
配置:
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 匹配 :請(qǐng)求 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 匹配 :請(qǐng)求 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 匹配 :請(qǐng)求 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 匹配 :請(qǐng)求 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 匹配 :請(qǐng)求來(lái)源 IP 在指定范圍內(nèi)
配置:
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
我們先來(lái)看下 Spring Cloud Gateway 官網(wǎng)提供的架構(gòu)圖:

上一節(jié)講完了常見分類的 Predicate 匹配規(guī)則,客戶端發(fā)送請(qǐng)求過(guò)來(lái),通過(guò) HandlerMapping 進(jìn)行 predicate 的匹配,匹配成功再進(jìn)行下面的處理。
3.1 org.springframework.web.reactive.DispatcherHandler
接收到請(qǐng)求,匹配 HandlerMapping ,此處會(huì)匹配到 RoutePredicateHandlerMapping。由于 Gateway 是構(gòu)建在 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?獲得對(duì)應(yīng)的?WebHandler?
????????Flux.fromIterable(this.handlerMappings).concatMap((mapping)?->?{
????????????//?獲得?Handler?
????????????return?mapping.getHandler(exchange);
????????//?如果匹配不到 WebHandler ,返回 HANDLER_NOT_FOUND_EXCEPTION 。
????????}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler)?->?{
????????????//?調(diào)用 invokeHandler()?方法,執(zhí)行 Handler 。
????????????return?this.invokeHandler(exchange,?handler);
????????}).flatMap((result)?->?{
????????????//?調(diào)用?handleResult()?方法,處理結(jié)果
????????????return?this.handleResult(exchange,?result);
????????});
????}
????...
}
繼續(xù)跟一下 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();
????????????//?調(diào)用support方法?,是否支持?WebHandler
????????????if?(handlerAdapter.supports(handler))?{
????????????????//?調(diào)用handle?方法,執(zhí)行處理器
????????????????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;
????//?執(zhí)行處理器。例如,WebHandler 為 FilteringWebHandler 時(shí),獲得 Route 的 GatewayFilter 數(shù)組,創(chuàng)建 GatewayFilterChain 處理請(qǐng)求。
????Mono<Void>?mono?=?webHandler.handle(exchange);
????//?在 WebHandler 執(zhí)行完后?#then(Mongo),然后返回 Mono.empty()?。
????return?mono.then(Mono.empty());
}
SimpleHandlerAdapter 返回的是 Mono.empty() ,所以不會(huì)觸發(fā)該方法。
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
接收到請(qǐng)求,匹配 Route ,并返回處理 Route 的 FilteringWebHandler。
SimpleHandlerAdapter#handle(ServerWebExchange, Object) 調(diào)用 FilteringWebHandler#handle(ServerWebExchange) 方法,處理請(qǐng)求。
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?{
????????????//?設(shè)置?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);
????????????????}
????????????????//?設(shè)置?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 匹配路由,這個(gè)方法是網(wǎng)關(guān)的核心,像我們自研的網(wǎng)關(guān),如果你剛接手公司中的網(wǎng)關(guān)項(xiàng)目,找到匹配路由再展開,能幫你省很多時(shí)間,快速熟悉公司中網(wǎng)關(guān)的項(xiàng)目。
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());
????????????//?并調(diào)用 Predicate#apply(ServerWebExchange)?方法,順序匹配一個(gè) Route。
????????????return?(Publisher)r.getPredicate().apply(exchange);
????????//?未來(lái)會(huì)增加匹配過(guò)程中發(fā)生異常的處理。目前,任何一個(gè) Predicate#test(ServerWebExchange)?的方法調(diào)用發(fā)生異常時(shí),都會(huì)導(dǎo)致匹配不到 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 數(shù)組,創(chuàng)建 GatewayFilterChain 處理請(qǐng)求。這里我們放到下一篇來(lái)講,下一篇也很重要,從原理來(lái)說(shuō)也不是很難理解,就是一個(gè)過(guò)濾器鏈。但從 Gateway 的兩大核心:路由+過(guò)濾鏈來(lái)說(shuō),這又很重要。
歡迎大家關(guān)注我的公眾號(hào)【老周聊架構(gòu)】,Java后端主流技術(shù)棧的原理、源碼分析、架構(gòu)以及各種互聯(lián)網(wǎng)高并發(fā)、高性能、高可用的解決方案。
喜歡的話,點(diǎn)贊、再看、分享三連。

點(diǎn)個(gè)在看你最好看
