Spring Cloud Gateway中多规则限流(RequestRateLimiter覆盖问题)
场景在SCG中配置多个RequestRateLimiter时,只能支持一个速率。源码分析跟踪源码发现package org.springframework.cloud.gateway.route;...public class RouteDefinitionRouteLocatorimplements RouteLocator, BeanFactoryAware, ApplicationEvent
·
场景
在SCG中配置多个RequestRateLimiter时,只能支持一个速率。
源码分析
跟踪源码发现下列两个关键类。
AbstractRateLimiter:初始化rarelimiter配置。
RequestRateLimiterGatewayFilterFactory:限流过滤器,包括定时刷新过滤器内容,和请求触发执行限流判断。
package org.springframework.cloud.gateway.filter.ratelimit;
...
public abstract class AbstractRateLimiter<C> extends AbstractStatefulConfigurable<C>
implements RateLimiter<C>, ApplicationListener<FilterArgsEvent> {
...
@Override
public void onApplicationEvent(FilterArgsEvent event) {
Map<String, Object> args = event.getArgs();
if (args.isEmpty() || !hasRelevantKey(args)) {
return;
}
//获取路由id,包括默认的defaultFilters
String routeId = event.getRouteId();
//使用routeId+KeyResolver.hashcode作为配置id,防止重复
routeId = routeId + event.getArgs().get("key-resolver").hashCode();
C routeConfig = newConfig();
if (this.configurationService != null) {
this.configurationService.with(routeConfig)
.name(this.configurationPropertyName).normalizedProperties(args)
.bind();
}
//更新速率配置集合,routeId一致会覆盖
getConfig().put(routeId, routeConfig);
}
...
}
package org.springframework.cloud.gateway.filter.factory;
...
/**
* User Request Rate Limiter filter. See https://stripe.com/blog/rate-limiters and
* https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d#file-1-check_request_rate_limiter-rb-L11-L34.
*/
@ConfigurationProperties("spring.cloud.gateway.filter.request-rate-limiter")
public class RequestRateLimiterGatewayFilterFactory extends
AbstractGatewayFilterFactory<RequestRateLimiterGatewayFilterFactory.Config> {
...
@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));
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);
}
//获取routeid
String routeId = config.getRouteId();
//使用routeId+KeyResolver.hashcode获取config
routeId = routeId + config.getKeyResolver().hashCode();
if (routeId == null) {
Route route = exchange
.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
routeId = route.getId();
}
//根据routeid获取limiter中配置
return limiter.isAllowed(routeId, key).flatMap(response -> {
for (Map.Entry<String, String> header : response.getHeaders()
.entrySet()) {
exchange.getResponse().getHeaders().add(header.getKey(),
header.getValue());
}
if (response.isAllowed()) {
return chain.filter(exchange);
}
setResponseStatus(exchange, config.getStatusCode());
return exchange.getResponse().setComplete();
});
});
}
}
修改方案
新建上述两个文件,使用本地代码覆盖jar包源码,包名一致则可优先使用本地。
效果如下:
更多推荐
已为社区贡献19条内容
所有评论(0)