SpringCloud Gateway——谓词GatewayPredicate
1. 简介
路由配置:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: app
uri: lb://nacos-app
predicates:
- Path=/app/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@routeIdKeyResolver}"
其中predicates
是Gateway
中的谓词,用来筛选属于哪个路由,可以配置多个,只有通过了配置的所有谓词才能使用对应的路由配置。
而谓词的实现跟上篇文章路由过滤器GatewayFilter
很相似,接下来通过源码进行分析。
2. 源码分析
1. RoutePredicateFactory
RoutePredicateFactory
是生成GatewayPredicate
的工厂类接口,这里以最常用的PathRoutePredicateFactory
为例。
PathRoutePredicateFactory
:路径匹配谓词
@Override
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);
});
}
// 定义GatewayPredicate实现
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// 解析路径
PathContainer path = parsePath(
exchange.getRequest().getURI().getRawPath());
// 匹配则返回true,否则返回false
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());
}
};
}
每一个RoutePredicateFactory
实现主要逻辑在apply()
中,在该方法中定义GatewayPredicate
接口逻辑。
在GatewayAutoConfiguration
中实例化了RoutePredicateFactory
的每一种实现
GatewayAutoConfiguration
......
@Bean
public PathRoutePredicateFactory pathRoutePredicateFactory() {
return new PathRoutePredicateFactory();
}
@Bean
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
return new QueryRoutePredicateFactory();
}
@Bean
public ReadBodyPredicateFactory readBodyPredicateFactory(
ServerCodecConfigurer codecConfigurer) {
return new ReadBodyPredicateFactory(codecConfigurer.getReaders());
}
@Bean
public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
return new RemoteAddrRoutePredicateFactory();
}
......
2. RouteDefinitionRouteLocator
RouteDefinitionRouteLocator
用于将路由配置解析成Route
对象,其中就包括谓词
解析。
GatewayAutoConfiguration
:实例化RouteDefinitionRouteLocator
,传入RoutePredicateFactory
实例集合
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
RouteDefinitionRouteLocator
-
初始化谓词集合predicates
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties,
ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = configurationService;
// 初始化谓词集合
initFactories(predicates);
gatewayFilterFactories.forEach(
factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
private void initFactories(List<RoutePredicateFactory> predicates) {
// 以factory.name()为key,factory为value存入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 + "]");
}
});
}
初始化RouteDefinitionRouteLocator
时,将谓词按name()
为key,实例为value存入predicates
变量中,来看看name()
逻辑
RoutePredicateFactory
default String name() {
return NameUtils.normalizeRoutePredicateName(getClass());
}
NameUtils
public static String normalizeRoutePredicateName(
Class<? extends RoutePredicateFactory> clazz) {
// 去掉类名中的RoutePredicateFactory
return removeGarbage(clazz.getSimpleName()
.replace(RoutePredicateFactory.class.getSimpleName(), ""));
}
private static String removeGarbage(String s) {
int garbageIdx = s.indexOf("$Mockito");
if (garbageIdx > 0) {
return s.substring(0, garbageIdx);
}
return s;
}
可以看出是去掉类名中的RoutePredicateFactory做为key,这里就对应上了路由配置,比如PathRoutePredicateFactory
predicates:
- Path=/app/**
-
解析路由配置中的谓词
RouteDefinitionRouteLocator
@Override
public Flux<Route> getRoutes() {
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute);
......
}
private Route convertToRoute(RouteDefinition routeDefinition) {
// 谓词
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
......
}
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
if (predicates == null || predicates.isEmpty()) {
return AsyncPredicate.from(exchange -> true);
}
// 查找第一个谓词实现
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
andPredicate);
// AsyncPredicate是一个链表结构,and()生成AndAsyncPredicate对象表示需要符合所有谓词才匹配
predicate = predicate.and(found);
}
return predicate;
}
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
PredicateDefinition predicate) {
// 根据名称从predicates变量中查找谓词实现
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find RoutePredicateFactory with name "
+ predicate.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + route.getId() + " applying "
+ predicate.getArgs() + " to " + predicate.getName());
}
// @formatter:off
Object config = this.configurationService.with(factory)
.name(predicate.getName())
.properties(predicate.getArgs())
.eventFunction((bound, properties) -> new PredicateArgsEvent(
RouteDefinitionRouteLocator.this, route.getId(), properties))
.bind();
// @formatter:on
return factory.applyAsync(config);
}
这里通过名称从predicates
变量中获取谓词实现,然后调用RoutePredicateFactory.applyAsync()
生成AsyncPredicate
实例,AsyncPredicate
是一个记录左右节点的链表结构,所以最后将多个谓词通过and()
合并为一个AsyncPredicate
实例。
RoutePredicateFactory.applyAsync()
:调用RoutePredicateFactory.apply()
的具体实现,然后封装成AsyncPredicate
,所以谓词的实现类需要重写apply()
default AsyncPredicate<ServerWebExchange> applyAsync(C config) {
return toAsyncPredicate(apply(config));
}
Predicate<ServerWebExchange> apply(C config);
ServerWebExchangeUtils
public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(
Predicate<? super ServerWebExchange> predicate) {
Assert.notNull(predicate, "predicate must not be null");
return AsyncPredicate.from(predicate);
}
AsyncPredicate
:用于保存谓词集合的数据结构
class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {
private final Predicate<T> delegate;
public DefaultAsyncPredicate(Predicate<T> delegate) {
this.delegate = delegate;
}
@Override
public Publisher<Boolean> apply(T t) {
// 执行GatewayPredicate.test()
return Mono.just(delegate.test(t));
}
@Override
public String toString() {
return this.delegate.toString();
}
}
default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
return new AndAsyncPredicate<>(this, other);
}
class AndAsyncPredicate<T> implements AsyncPredicate<T> {
private final AsyncPredicate<? super T> left;
private final AsyncPredicate<? super T> right;
public AndAsyncPredicate(AsyncPredicate<? super T> left,
AsyncPredicate<? super T> right) {
Assert.notNull(left, "Left AsyncPredicate must not be null");
Assert.notNull(right, "Right AsyncPredicate must not be null");
this.left = left;
this.right = right;
}
@Override
public Publisher<Boolean> apply(T t) {
return Mono.from(left.apply(t)).flatMap(
result -> !result ? Mono.just(false) : Mono.from(right.apply(t)));
}
}
调用AsyncPredicate.and()
会生成AndAsyncPredicate
对象,AndAsyncPredicate
是一个链表结构,保存着上一个(left)AsyncPredicate
和下一个(right)AsyncPredicate
,当执行apply()
时,会从上往下执行对应实例的apply()
,只有通过所有的谓词才返回true。而每一个实例都是一个DefaultAsyncPredicate
,其中的apply()
调用了GatewayPredicate.test()
,所以在定义GatewayPredicate
的逻辑时为什么要重写test()
。
3. RoutePredicateHandlerMapping
那什么时候执行谓词的apply()
呢?在请求转发源码分析
一文中有个流程图
请求转发的流程是,根据谓词筛选出对应的路由,然后执行相应的过滤器。从类名中可以看出RoutePredicateHandlerMapping
是根据谓词筛选路由的逻辑。
RoutePredicateHandlerMapping
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
......
// 查找对应的Route
return lookupRoute(exchange)
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isDebugEnabled()) {
logger.debug(
"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
}
// 将对应的Route做为一个属性放到exchange
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
// 返回FilteringWebHandler
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) + "]");
}
})));
}
/**
* 查找符合条件的Route
* @param exchange
* @return
*/
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
.concatMap(route -> Mono.just(route).filterWhen(r -> {
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
// 执行谓词,是否符合
return r.getPredicate().apply(exchange);
})
.doOnError(e -> logger.error(
"Error applying predicate for route: " + route.getId(),
e))
.onErrorResume(e -> Mono.empty()))
.next()
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
}
世界那么大,感谢遇见,未来可期…
欢迎同频共振的那一部分人
作者公众号:Tarzan写bug
原文始发于微信公众号(Tarzan写bug):SpringCloud Gateway——谓词原理
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/45884.html