Gateway实现自定义限流规则
有些同学在使用gateway配置限流规则,可能需要自定义一些规则对哪些请求不限流。常规的做法是参考
org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory
继承或者实现其接口,来重写其
apply()
或者
isAllowed()
如图是Spring Cloud Gateway 3.1.1的默认实现
如果需要自定义限流的逻辑仅限于决定谁被限流,谁不被限流的话,可以看图中下面这段代码
@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)) { //如果等于EMPTY_KEY
if (denyEmpty) { //且denyEmpty为true,拦截请求
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();
}
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();
});
});
}
这里先判断了key值是否与EMPTY_KEY 相等
EMPTY_KEY 值如下:
private static final String EMPTY_KEY = "____EMPTY_KEY__";
又判断了变量denyEmpty的boolean值,如果为false则直接放行,不限流
所以我们可以通过调整denyEmpty的值为false,再将无需限流的请求key设置为字符串"__EMPTY_KEY",即可实现自定义限流 ~
首先是设置key,我们调整KeyResolver的逻辑,如下:
@Configuration
public class Config {
private static final String EMPTY_KEY = "____EMPTY_KEY__";
@Bean
KeyResolver hostAddressKeyResolver() {
return exchange -> {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
if(serverHttpRequest.getHeaders().containsKey("此处举例,该逻辑替换成你需要的逻辑")){
return Mono.just(EMPTY_KEY);
}
String key = Objects.requireNonNull(serverHttpRequest.getRemoteAddress()).getAddress().getHostAddress();//假设你也是根据host限流
return Mono.just(key);
};
}
}
其次是denyEmpty的值,denyEmpty来源于配置文件,默认为true,源码如下
private boolean denyEmptyKey = true;//默认值
@Override
public GatewayFilter apply(Config config) {
...
boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
...
}
private <T> T getOrDefault(T configValue, T defaultValue) {
return (configValue != null) ? configValue : defaultValue;
}
所以我们可以调整如下配置
spring:
cloud:
gateway:
routes:
- id: ...
uri: ...
predicates:
...
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@自定义}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
deny-empty-key: false
这样,对于我们 Mono.just(EMPTY_KEY)的请求,就可以实现不限流了。
以上代码仅基于Spring Cloud Gateway 3.1.1版本,供大家参考