gateway添加限流类
This commit is contained in:
parent
27cbcd9f33
commit
2506d9265f
|
|
@ -5,11 +5,13 @@ import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@MapperScan("com.tacit.admin.mapper")
|
@MapperScan("com.tacit.admin.mapper")
|
||||||
@EnableDiscoveryClient
|
@EnableDiscoveryClient
|
||||||
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
||||||
|
@ComponentScan(basePackages = {"com.tacit.admin", "com.tacit.common"})
|
||||||
public class AdminApplication {
|
public class AdminApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(AdminApplication.class, args);
|
SpringApplication.run(AdminApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@MapperScan("com.tacit.app.mapper")
|
@MapperScan("com.tacit.app.mapper")
|
||||||
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
@EnableFeignClients(basePackages = "com.tacit.common.feign")
|
||||||
|
@ComponentScan(basePackages = {"com.tacit.app", "com.tacit.common"})
|
||||||
public class AppApiApplication {
|
public class AppApiApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(AppApiApplication.class, args);
|
SpringApplication.run(AppApiApplication.class, args);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.tacit.gateway.config;
|
||||||
|
|
||||||
|
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||||
|
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限流配置类
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RateLimiterConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP限流 - 根据请求IP地址进行限流
|
||||||
|
* 添加@Primary注解,作为默认的KeyResolver
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public KeyResolver ipKeyResolver() {
|
||||||
|
return exchange -> {
|
||||||
|
// 处理可能的空指针异常
|
||||||
|
var remoteAddress = exchange.getRequest().getRemoteAddress();
|
||||||
|
if (remoteAddress != null && remoteAddress.getAddress() != null) {
|
||||||
|
return Mono.just(remoteAddress.getAddress().getHostAddress());
|
||||||
|
}
|
||||||
|
return Mono.just("unknown");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口限流 - 根据请求路径进行限流
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public KeyResolver apiKeyResolver() {
|
||||||
|
return exchange -> Mono.just(exchange.getRequest().getPath().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户限流 - 根据请求头中的X-User-Id进行限流
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public KeyResolver userKeyResolver() {
|
||||||
|
return exchange -> {
|
||||||
|
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
|
||||||
|
return Mono.just(userId != null ? userId : "anonymous");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务限流 - 根据路由ID进行限流
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public KeyResolver serviceKeyResolver() {
|
||||||
|
return exchange -> {
|
||||||
|
// 从路由中获取服务名称,使用正确的常量避免空指针
|
||||||
|
Object routeObj = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
|
||||||
|
if (routeObj != null) {
|
||||||
|
// 使用toString()方法获取路由信息,避免类型转换错误
|
||||||
|
return Mono.just(routeObj.toString());
|
||||||
|
}
|
||||||
|
return Mono.just("unknown");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.tacit.gateway.filter;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||||
|
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
|
||||||
|
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义限流异常处理过滤器
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class CustomRateLimitExceptionHandler extends AbstractGatewayFilterFactory<CustomRateLimitExceptionHandler.Config> {
|
||||||
|
|
||||||
|
public CustomRateLimitExceptionHandler() {
|
||||||
|
super(Config.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GatewayFilter apply(Config config) {
|
||||||
|
// 创建全局异常处理过滤器,优先级最高
|
||||||
|
GatewayFilter filter = new GatewayFilter() {
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
|
return chain.filter(exchange)
|
||||||
|
.onErrorResume(throwable -> {
|
||||||
|
// 检查是否是限流异常
|
||||||
|
if (throwable.getMessage() != null && throwable.getMessage().contains("Rate limit exceeded")) {
|
||||||
|
log.warn("限流异常: {}, 请求路径: {}", throwable.getMessage(), exchange.getRequest().getPath());
|
||||||
|
return handleRateLimitException(exchange);
|
||||||
|
}
|
||||||
|
// 其他异常继续抛出
|
||||||
|
return Mono.error(throwable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置过滤器优先级,确保在其他过滤器之前执行
|
||||||
|
return new OrderedGatewayFilter(filter, Ordered.HIGHEST_PRECEDENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理限流异常,返回自定义响应
|
||||||
|
*/
|
||||||
|
private Mono<Void> handleRateLimitException(ServerWebExchange exchange) {
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
|
||||||
|
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
|
||||||
|
// 自定义限流响应内容
|
||||||
|
String responseBody = "{\"code\": 429, \"message\": \"请求过于频繁,请稍后再试\", \"data\": null}";
|
||||||
|
|
||||||
|
return response.writeWith(Mono.just(response.bufferFactory().wrap(responseBody.getBytes())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Config {
|
||||||
|
// 可以添加配置属性
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue