Spring-Cloud-Gateway路由選擇是通過Predicate函數(shù)式接口進(jìn)行判斷當(dāng)前路由是否滿足給定條件畏邢。下來(lái)閱讀Spring-Cloud-Gateway路由的Predicate構(gòu)建流程以及選擇路由的源碼
首先看下JDK8中Predicate接口定義
Predicate<T> 接受一個(gè)輸入?yún)?shù),返回一個(gè)布爾值結(jié)果检吆。該接口包含多種默認(rèn)方法來(lái)將Predicate組合成其他復(fù)雜的邏輯(比如:與舒萎,或,非)蹭沛”矍蓿可以用于接口請(qǐng)求參數(shù)校驗(yàn)摊灭、判斷新老數(shù)據(jù)是否有變化需要進(jìn)行更新操作咆贬。add--與、or--或帚呼、negate--非
- boolean test(T t); 判斷
- Predicate<T> and(Predicate<? super T> other) 接收一個(gè)Predicate類型掏缎,也就是將傳入的條件和當(dāng)前條件以并且(AND)的關(guān)系組合
- Predicate<T> or(Predicate<? super T> other)接收一個(gè)Predicate類型,也就是將傳入的條件和當(dāng)前條件以或(OR)的關(guān)系組合
- 加載路由中的Predicate
在路由定位器中以及看到了通過路由定義轉(zhuǎn)換路由方法煤杀,其中包含了通過謂語(yǔ)定義(PredicateDefinition)轉(zhuǎn)換謂語(yǔ)(Predicate)的部分,在RouteDefinitionRouteLocator類中源碼如下:
/**
* 返回組合的謂詞
* @param routeDefinition
* @return
*/
private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
//獲取RouteDefinition中的PredicateDefinition集合
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
//返回一個(gè)組合的謂詞眷蜈,表示該謂詞與另一個(gè)謂詞的短路邏輯AND
predicate = predicate.and(found);
}
return predicate;
}
/**
* 獲取一個(gè)謂語(yǔ)定義(PredicateDefinition)轉(zhuǎn)換的謂語(yǔ)
* @param route
* @param predicate
* @return
*/
@SuppressWarnings("unchecked")
private Predicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
//獲取謂語(yǔ)創(chuàng)建工廠
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
}
//獲取參數(shù)
Map<String, String> args = predicate.getArgs();
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + route.getId() + " applying "
+ args + " to " + predicate.getName());
}
//組裝參數(shù)
Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
//構(gòu)建創(chuàng)建謂語(yǔ)的配置信息
Object config = factory.newConfig();
ConfigurationUtils.bind(config, properties,
factory.shortcutFieldPrefix(), predicate.getName(), validator);
if (this.publisher != null) {
this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));
}
//通過謂語(yǔ)工廠構(gòu)建謂語(yǔ)
return factory.apply(config);
}
- 獲取路由定義(routeDefinition)所有的謂語(yǔ)定位(PredicateDefinition)
- 以此根據(jù)謂語(yǔ)定義(PredicateDefinition)查找謂語(yǔ)對(duì)于的創(chuàng)建工廠(RoutePredicateFactory) 創(chuàng)建謂語(yǔ)
- 通過 Predicate<T>接口 and方法合并謂語(yǔ)集合返回一個(gè)新的復(fù)合謂語(yǔ)
- 使用路由Predicate判斷路由是否可用
在Spring-Cloud-Gateway之請(qǐng)求處理流程里我們已經(jīng)了解在handlerMapping中通過路由定位器獲取所有路由,并過濾掉謂語(yǔ)判斷失敗的路由沈自,最終獲取滿足條件的路由酌儒,下面再看下過濾的具體邏輯。
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
//通過路由定位器獲取路由信息
return this.routeLocator.getRoutes()
.filter(route -> {
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, route.getId());
//返回通過謂語(yǔ)過濾的路由信息
return route.getPredicate().test(exchange);
})
}
- 通過routeLocator的getRoutes方法獲取所有路由
- 在所有路由中通過filter方法匹配Predicate.test匹配的路由信息
通過上面1枯途,2兩步我們看到了整個(gè)路由的條件創(chuàng)建以及使用的地方以及流程今豆,Spring-Cloud-Gateway通過Predicate接口完成簡(jiǎn)單條件組合以及判斷。
接下來(lái)看通過RoutePredicateFactory創(chuàng)建PredicateSpring-Cloud-Gateway預(yù)制了很多RoutePredicateFactory使其可以通過簡(jiǎn)單的配置就可以創(chuàng)建出理想的Predicate
RoutePredicateFactory類圖如下:
RoutePredicateFactory子類功能分類圖:
- MethodRoutePredicateFactory
/**
* 請(qǐng)求方式(GET,POST,DEL,PUT)校驗(yàn)匹配創(chuàng)建工廠
* @author Spencer Gibb
*/
public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> {
public static final String METHOD_KEY = "method";
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
//獲取當(dāng)前請(qǐng)求的HttpMethod
HttpMethod requestMethod = exchange.getRequest().getMethod();
//校驗(yàn)請(qǐng)求HttpMethod與配置是否一致
return requestMethod == config.getMethod();
};
}
public static class Config {
/**
* http 請(qǐng)求Method
*/
private HttpMethod method;
public HttpMethod getMethod() {
return method;
}
public void setMethod(HttpMethod method) {
this.method = method;
}
}
}
配置列子
spring:
cloud:
gateway:
routes:
# =====================================
- id: method_route
uri: http://example.org
predicates:
- Method=GET
- Method=GET 會(huì)被解析成PredicateDefinition對(duì)象 (name =Method 柔袁,args= GET)
- 通過PredicateDefinition的Name找到MethodRoutePredicateFactory工廠
- 通過 PredicateDefinition 的args 創(chuàng)建Config對(duì)象(HttpMethod=GET)
- 通過 MethodRoutePredicateFactory工廠的apply方法傳入config創(chuàng)建Predicate對(duì)象。
在此只對(duì)MethodRoutePredicateFactory做了描述和說(shuō)明异逐,其它的謂語(yǔ)工程基本與此一致捶索。此時(shí)Spring-Cloud-Gateway的RoutePredicate所有邏輯代碼都已經(jīng)閱讀完畢,對(duì)閱讀過程做文檔記錄以便加深印象灰瞻,后期翻閱