Gateway
分为全局过滤器GlobalFilter
和局部过滤器GatewayFilter
,局部过滤器只有在路由配置中针对路由ID配置了才会使用,对应配置中的filters
属性。
如:
spring:
application:
name: nacos-gateway
cloud:
gateway:
routes:
- id: app
uri: lb://nacos-app
predicates:
- Path=/app/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@routeIdKeyResolver}"
接下来通过源码来分析局部过滤器的实现原理。
Gateway
内置的路由过滤器实现往往很少直接实现GatewayFilter
接口,而是通过GatewayFilterFactory
这个工厂类生成。这里以上篇限流文章
中的RequestRateLimiterGatewayFilterFactory
为例
RequestRateLimiterGatewayFilterFactory
@SuppressWarnings("unchecked")
@Override
public GatewayFilter apply(Config config) {
KeyResolver resolver = getOrDefault(config.keyResolver, defaultKeyResolver);
RateLimiter<Object> limiter = getOrDefault(config.rateLimiter,
defaultRateLimiter);
boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
HttpStatusHolder emptyKeyStatus = HttpStatusHolder
.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
// 调用KeyResolver.resolve()解析出限流的维度
return (exchange, chain) -> resolver.resolve(exchange).defaultIfEmpty(EMPTY_KEY)
.flatMap(key -> {
// 没找到则通过过滤器
if (EMPTY_KEY.equals(key)) {
if (denyEmpty) {
setResponseStatus(exchange, emptyKeyStatus);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
String routeId = config.getRouteId();
if (routeId == null) {
Route route = exchange
.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
routeId = route.getId();
}
// 调用RateLimiter.isAllowed()判断是否允许访问
return limiter.isAllowed(routeId, key).flatMap(response -> {
// 返回的response中的header加入到exchange header中
for (Map.Entry<String, String> header : response.getHeaders()
.entrySet()) {
exchange.getResponse().getHeaders().add(header.getKey(),
header.getValue());
}
// 允许访问则通过过滤器
if (response.isAllowed()) {
return chain.filter(exchange);
}
// 被限流,返回HTTP状态码429
setResponseStatus(exchange, config.getStatusCode());
return exchange.getResponse().setComplete();
});
});
}
GatewayFilterFactory
中apply()
是用来生成GatewayFilter
的,所以可以看到RequestRateLimiterGatewayFilterFactory
的apply()
定义了限流逻辑的GatewayFilter
,那为什么是apply()
呢?接着往下看
内置的GatewayFilterFactory
实现都是在哪里实例化的呢?
GatewayAutoConfiguration
:实例化了所有内置GatewayFilterFactory
实现
@Bean
@ConditionalOnBean({ RateLimiter.class, KeyResolver.class })
public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(
RateLimiter rateLimiter, KeyResolver resolver) {
return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
}
@Bean
public RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory() {
return new RewritePathGatewayFilterFactory();
}
@Bean
public RetryGatewayFilterFactory retryGatewayFilterFactory() {
return new RetryGatewayFilterFactory();
}
@Bean
public SetPathGatewayFilterFactory setPathGatewayFilterFactory() {
return new SetPathGatewayFilterFactory();
}
有了GatewayFilterFactory中定义的路由过滤器,在哪里使用到他们的呢?
RouteDefinitionRouteLocator
是路由解析器,将路由定义转换为Route
对象,其中需要解析路由配置中定义的过滤器,这里就用到了GatewayFilterFactory
.
RouteDefinitionRouteLocator
@Override
public Flux<Route> getRoutes() {
Flux<Route> 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<ServerWebExchange> predicate = combinePredicates(routeDefinition);
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
@SuppressWarnings("unchecked")
List<GatewayFilter> loadGatewayFilters(String id,
List<FilterDefinition> filterDefinitions) {
// 过滤器定义转为GatewayFilter
ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
for (int i = 0; i < filterDefinitions.size(); i++) {
FilterDefinition definition = filterDefinitions.get(i);
// 根据名称筛选出对应的GatewayFilterFactory实现
GatewayFilterFactory factory = this.gatewayFilterFactories
.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find GatewayFilterFactory with name "
+ definition.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter "
+ definition.getArgs() + " to " + definition.getName());
}
// 构造Config
Object configuration = this.configurationService.with(factory)
.name(definition.getName())
.properties(definition.getArgs())
.eventFunction((bound, properties) -> new FilterArgsEvent(
RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
.bind();
if (configuration instanceof HasRouteId) {
HasRouteId hasRouteId = (HasRouteId) configuration;
hasRouteId.setRouteId(id);
}
// 调用GatewayFilterFactory.apply()生成GatewayFilter
GatewayFilter gatewayFilter = factory.apply(configuration);
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
}
else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
}
return ordered;
}
这里就用到了GatewayFilterFactory.apply()
.
根据名称筛选出GatewayFilterFactory
实现是怎么做的呢?也就是怎样根据配置中的RequestRateLimiter
找到RequestRateLimiterFactory
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@routeIdKeyResolver}"
首先要看RouteDefinitionRouteLocator.gatewayFilterFactories
的初始化,
GatewayAutoConfiguration
@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
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
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;
}
将之前实例化GatewayFilterFactory
的Bean集合按GatewayFilterFactory.name()
为key,Bean为value加入到gatewayFilterFactories
.
GatewayFilterFactory.name()
default String name() {
return NameUtils.normalizeFilterFactoryName(getClass());
}
NameUtils
public static String normalizeFilterFactoryName(
Class<? extends GatewayFilterFactory> clazz) {
// 去掉类名中的GatewayFilterFactory
return removeGarbage(clazz.getSimpleName()
.replace(GatewayFilterFactory.class.getSimpleName(), ""));
}
private static String removeGarbage(String s) {
int garbageIdx = s.indexOf("$Mockito");
if (garbageIdx > 0) {
return s.substring(0, garbageIdx);
}
return s;
}
SO,这里通过去掉类名中的GatewayFilterFactory,对应上配置中的名称。
世界那么大,感谢遇见,未来可期…
欢迎同频共振的那一部分人
作者公众号:Tarzan写bug