Gateway服务限流配置
背景
限流作用就不说了,往往都是防止一些恶意请求,无限制请求接口导致服务处理时间过长,继而导致响应延迟,服务阻塞等等,所以会对高频率的一些接口添加限流这样的功能。
刚好gateway刚好自带了服务限流功能,所以我们就用gateway实现此功能。
编写一个限流规则
1、添加RateLimiterConfig配置文件
package org.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
@Configuration
@Slf4j
public class RateLimiterConfig {
RateLimiterConfig(){
log.info("限流配置加载");
}
/**
* 根据用户ip限流规则
* @return
*/
@Primary
@Bean(value = "remoteAddrKeyResolver")
public KeyResolver remoteAddrKeyResolver(){
return exchange -> {
String hostAddress=exchange.getRequest()
.getRemoteAddress()
.getAddress()
.getHostAddress();
System.out.println(hostAddress);
return Mono.just(hostAddress);
};
}
/**
* 根据Path限流
*
* @return key
*/
@Bean(value = "pathKeyResolver")
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().toString());
}
}
2、编写配置文件让配置文件扫描刚刚添加的bean
spring:
cloud:
gateway:
discovery:
locator:
enabled: false
routes:
# 商品模块
- id: Commodity #路由id,唯一
uri: lb://Commodity
predicates:
- Path=/commodity/** #以city开头的请求都负载到consumer服务
filters:
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 5
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@remoteAddrKeyResolver}"
注意:
- 那个服务需要限流就在那个服务下加入filter的配置即可,如果不需要限流则不需要添加
- name字段必须为
RequestRateLimiter
- key-resolver参数对应注入到Spring中Bean的名称。
但是我们发现请求后如果用户请求接口到达上限制返回的是1,这让前端不知道是后端发生了什么异常,这并不符合我们的设计要求。
所以服务升级
|
|
|
\|/
自定义返回信息配置类
添加GatewayRequestRateLimiterGatewayFilterFactory自定义异常类
package org.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/***
* @限流自定义
* @author CMS
*/
@Slf4j
@Component
public class GatewayRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {
private final RateLimiter defaultRateLimiter;
private final KeyResolver defaultKeyResolver;
public GatewayRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
super(defaultRateLimiter, defaultKeyResolver);
this.defaultRateLimiter = defaultRateLimiter;
this.defaultKeyResolver = defaultKeyResolver;
log.info("限流自定义返回加载");
}
@Override
public GatewayFilter apply(Config config) {
KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
return (exchange, chain) -> resolver.resolve(exchange).flatMap(key -> {
String routeId = config.getRouteId();
if (routeId == null) {
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
routeId = route.getId();
}
String finalRouteId = routeId;
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);
}
log.warn("已限流: {}", finalRouteId);
ServerHttpResponse httpResponse = exchange.getResponse();
//修改code为500
httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
if (!httpResponse.getHeaders().containsKey("Content-Type")) {
httpResponse.getHeaders().add("Content-Type", "application/json");
}
//此处无法触发全局异常处理,手动返回
DataBuffer buffer = httpResponse.bufferFactory().wrap(("{\n"
+ " \"code\": \"1414\","
+ " \"message\": \"服务器限流\","
+ " \"data\": \"Server throttling\","
+ " \"success\": false"
+ "}").getBytes(StandardCharsets.UTF_8));
return httpResponse.writeWith(Mono.just(buffer));
});
});
}
private <T> T getOrDefault(T configValue, T defaultValue) {
return (configValue != null) ? configValue : defaultValue;
}
}
接着修改我们的配置文件
spring:
cloud:
gateway:
discovery:
locator:
enabled: false
routes:
# 商品模块
- id: Commodity #路由id,唯一
uri: lb://Commodity
predicates:
- Path=/commodity/** #以city开头的请求都负载到consumer服务
filters:
- name: GatewayRequestRateLimiter #修改此处,改成我们自定义的返回信息配置类
args:
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 5
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@remoteAddrKeyResolver}"
最后我们的限流工作就ok了。
温馨小提示:gateway默认的限流规则只能有一个,如果需要配置两个可以参考–>(3条消息) Spring Cloud Gateway 限流适配多规则的解决方案_Suremotoo丶小小书童的博客-CSDN博客的文章。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/160677.html