官网:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-request-predicates-factories
spring-cloud-gateway核心配置类,里面初始化了很多核心bean,列出部分:
GatewayProperties
NettyConfiguration
RouteDefinitionLocator
PropertiesRouteDefinitionLocator
RouteLocator
RouteRefreshListener
RoutePredicateFactory
RoutePredicateHandlerMapping
FilteringWebHandler
AdaptCachedBodyGlobalFilter
RetryGatewayFilterFactory
PrefixPathGatewayFilterFactory
GatewayControllerEndpoint
细说路由route、谓词predicate、过滤器filter的初始化
spring cloud gateway 在初始化时 通过 GatewayAutoConfiguration 来初始化各种其内置的 predicate、filter 和 RouteDefinitionRouteLocator(它最终还是服务于cachedCompositeRouteLocator,缓存的RouteLocator) 。
谓词predicate
// Predicate Factory beans
@Bean
public AfterRoutePredicateFactory afterRoutePredicateFactory() {
return new AfterRoutePredicateFactory();
}
@Bean
public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
return new BeforeRoutePredicateFactory();
}
@Bean
public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
return new BetweenRoutePredicateFactory();
}
....其余省略
官网给出大部分predicate的用法
过滤器filter
// GatewayFilter Factory beans
@Bean
public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() {
return new AddRequestHeaderGatewayFilterFactory();
}
@Bean
public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() {
return new MapRequestHeaderGatewayFilterFactory();
}
@Bean
public AddRequestParameterGatewayFilterFactory addRequestParameterGatewayFilterFactory() {
return new AddRequestParameterGatewayFilterFactory();
}
....其余省略
初始化
可以看到 RouteDefinitionRouteLocator 中,收集了所有predicate和filter,并且用map来装载,名字则是它们各自的首个单词,如BeforeRoutePredicateFactory,放进map则key是Before,value则是该对象,filter同理。
上述收集所有的predicate、filter,用作匹配用户在application配置文件中所定义的predicateDefinition和filterDefinition,而用户定义的(写在application配置文件)则在GatewayProperties中,同时我们写在配置文件中的也仅仅是map的key,匹配上就可以拿到该RoutePredicateFactory去处理。
RouteDefinitionRouteLocator初始化
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List gatewayFilters,
List predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
-----------------------------------------------
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List predicates,
List 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 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 + "]");
}
});
}
GatewayProperties,则是我们用户定义的route,以及route下的filter和predicate。
@ConfigurationProperties("spring.cloud.gateway")
@Validated
public class GatewayProperties {
private final Log logger = LogFactory.getLog(getClass());
/**
* List of Routes.
*/
@NotNull
@Valid
private List routes = new ArrayList<>();
/**
* List of filter definitions that are applied to every route.
*/
private List defaultFilters = new ArrayList<>();
private List streamingMediaTypes = Arrays
.asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
....其余省略
----------------------------------------------------------------------------------
@Validated
public class RouteDefinition {
private String id;
@NotEmpty
@Valid
private List predicates = new ArrayList<>();
@Valid
private List filters = new ArrayList<>();
@NotNull
private URI uri;
private Map metadata = new HashMap<>();
private int order = 0;
public RouteDefinition() {
}
public RouteDefinition(String text) {
int eqIdx = text.indexOf('=');
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse RouteDefinition text '" + text
+ "'" + ", must be of the form name=value");
}
setId(text.substring(0, eqIdx));
String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");
setUri(URI.create(args[0]));
...其余省略
application.yml 举例对照
spring:
cloud:
gateway:
routes: #配置路由
- id: consumer
uri: lb://consumer
predicates:
- Path=/cs/**
filters: # 网关过滤器
# 将 /cs/xx 重写为 /xx,再拼接在uri后面
#- RewritePath=/cs(?/?.*), $\{haha}
- StripPrefix=1
展开说说PredicateDefinition
@Validated
public class PredicateDefinition {
@NotNull
private String name;
private Map args = new LinkedHashMap<>();
public PredicateDefinition() {
}
public PredicateDefinition(String text) {
int eqIdx = text.indexOf('=');
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse PredicateDefinition text '"
+ text + "'" + ", must be of the form name=value");
}
setName(text.substring(0, eqIdx));
String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");
for (int i = 0; i < args.length; i++) {
this.args.put(NameUtils.generateName(i), args[i]);
}
}
}
以上是PredicateDefinition 的定义,我们可以看见有name和args,name就是predicate的名字,args是参数,它们在配置中用=号分割开来,args用,号分割,分成N个token,以键值对:_genkey_0, arg1和_genkey_1, arg2的形式解析,其实是key-value来的,后续讲到。
举例:
- id: timeout
uri: http://127.0.0.1
predicates:
- Path=/xx/**
- Method=GET
- Query=color,red
Query就是name,代表QueryRoutePredicateFactory,color,red就是value,代表参数中必须有color这个参数,而且要等于red,才会匹配上,如果没有,red ,则代表请求路径中必须含有color参数即可。当然这种事简写写法,有详细的,请参照官网https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#the-query-route-predicate-factory
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- name: Cookie
args:
name: mycookie #必须是这个名字,后续讲到。
regexp: mycookievalue # 必须是这个名字,后续讲到。
等价于
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue
展开说说FilterDefinition
同理PredicateDefinition 。
展开说说 RouteDefinitionRouteLocator
在其类中有这么一段代码,是根据application文件配置的routeDefinition以及它旗下的predicateDefinition和filterDefinition,这些都在gatewayProperties属性中,之后创建一个个route,而它旗下的predicateDefinition和filterDefinition,在总的predicates和filters找到对应的类型 且赋予各个route各自配置的,用来生产的predicate(AsyncPredicate)和filter(GatewayFIlter)的factory,最后都是factory生产出predicate(AsyncPredicate)和filter(GatewayFIlter):
@Override
public Flux getRoutes() {
Flux routes = this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute);
if (!gatewayProperties.isFailOnRouteDefinitionError()) {
// instead of letting error bubble up, continue
routes = routes.onErrorContinue((error, obj) -> {
if (logger.isWarnEnabled()) {
logger.warn("RouteDefinition id " + ((RouteDefinition) obj).getId()
+ " will be ignored. Definition has invalid configs, "
+ error.getMessage());
}
});
}
return routes.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
private Route convertToRoute(RouteDefinition routeDefinition) {
AsyncPredicate predicate = combinePredicates(routeDefinition);
List gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
private AsyncPredicate combinePredicates(
RouteDefinition routeDefinition) {
List predicates = routeDefinition.getPredicates();
if (predicates == null || predicates.isEmpty()) {
// this is a very rare case, but possible, just match all
return AsyncPredicate.from(exchange -> true);
}
AsyncPredicate predicate = lookup(routeDefinition,
predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate found = lookup(routeDefinition,
andPredicate);
predicate = predicate.and(found);
}
return predicate;
}
@SuppressWarnings("unchecked")
private AsyncPredicate lookup(RouteDefinition route,
PredicateDefinition predicate) {
RoutePredicateFactory
public class Route implements Ordered {
private final String id;
private final URI uri;
private final int order;
private final AsyncPredicate predicate;
private final List gatewayFilters;
private final Map metadata;
@Deprecated
private Route(String id, URI uri, int order,
AsyncPredicate predicate,
List gatewayFilters) {
this(id, uri, order, predicate, gatewayFilters, new HashMap<>());
}
private Route(String id, URI uri, int order,
AsyncPredicate predicate,
List gatewayFilters, Map metadata) {
this.id = id;
this.uri = uri;
this.order = order;
this.predicate = predicate;
this.gatewayFilters = gatewayFilters;
this.metadata = metadata;
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(RouteDefinition routeDefinition) {
// @formatter:off
return new Builder().id(routeDefinition.getId())
.uri(routeDefinition.getUri())
.order(routeDefinition.getOrder())
.metadata(routeDefinition.getMetadata());
// @formatter:on
}
public static AsyncBuilder async() {
return new AsyncBuilder();
}
public static AsyncBuilder async(RouteDefinition routeDefinition) {
// @formatter:off
return new AsyncBuilder().id(routeDefinition.getId())
.uri(routeDefinition.getUri())
.order(routeDefinition.getOrder())
.metadata(routeDefinition.getMetadata());
// @formatter:on
}
里面的predicate配对问题, combinePredicates里的lookup方法,看着这一段,这里得出的config就是匹配上的predicateFactory里面的Config类,并执行apply,生产出predicate(AsyncPredicate)/filter(GatewayFIlter);filter也是相似的,getFilters()方法自行进入查看即可。
Object config = this.configurationService.with(factory)
.name(predicate.getName())
.properties(predicate.getArgs())
.eventFunction((bound, properties) -> new PredicateArgsEvent(
RouteDefinitionRouteLocator.this, route.getId(), properties))
.bind();
比如CookieRoutePredicateFactory和它的Config,注意它的name和regexp,和我们在上面给出的官网例子是一样的参数名字,规律是按照shortcutFieldOrder方法那样,展示方法里头的字段。
/**
* @author Spencer Gibb
*/
public class CookieRoutePredicateFactory
extends AbstractRoutePredicateFactory {
/**
* Name key.
*/
public static final String NAME_KEY = "name";
/**
* Regexp key.
*/
public static final String REGEXP_KEY = "regexp";
public CookieRoutePredicateFactory() {
super(Config.class);
}
@Override
public List shortcutFieldOrder() {
return Arrays.asList(NAME_KEY, REGEXP_KEY);
}
@Override
public Predicate apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
List 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);
}
};
}
@Validated
public static class Config {
@NotEmpty
private String name;
@NotEmpty
private String regexp;
public String getName() {
return name;
}
public Config setName(String name) {
this.name = name;
return this;
}
public String getRegexp() {
return regexp;
}
public Config setRegexp(String regexp) {
this.regexp = regexp;
return this;
}
}
}
参考文章:
spring cloud gateway 的 专题收录1
spring cloud gateway 的专题 收录2