Как реализовать ограничение скорости Spring Cloud Gateway для одного и того же API/URL с разными значениями ограничения скорости для разных пользователей?

У меня есть несколько микросервисов с Spring Cloud. Теперь мне нужно реализовать ограничение скорости для API на облачном шлюзе на основе имени пользователя.

С помощью KeyResolver я извлекаю пользователя из JWT-токена запрашивающей стороны. и реализовал RedisRateLimiter с тем же значением ограничения скорости (consumer50tps) для всех пользователей, как показано ниже. Он работает нормально.

Теперь требуется установить значения ограничения скорости на основе подписки пользователя, тогда как для одного и того же API/URL разные пользователи получают разные значения пределов (consumer50tps или Consumer100tps).

текущий код работает нормально для одного значения ограничения (потребитель 50 бит/с) для всех пользователей.

    @Bean
    @Primary
    public RedisRateLimiter consumer50tps() {
    return new RedisRateLimiter(50, 50, 1);
    }

    @Bean
    public RedisRateLimiter consumer100tps() {
    return new RedisRateLimiter(100, 100, 1);
    }

    @Bean
    public KeyResolver userKeyResolver1() {
    return exchange -> {
        // Implement logic to fetch JWT and get user name
    };
    }

    @Bean
    public RouteLocator appRouteConfig(RouteLocatorBuilder builder) {
    return builder.routes()
            .route(p -> p.path("/microservice-one/api1/**")
                    .filters(f -> f
                            .rewritePath("/microservice-one/(?<segment>.*)", "/${segment}")
                            .requestRateLimiter(r -> r.setRateLimiter(consumer50tps())
                                    .setKeyResolver(userKeyResolver())
                            ))
                    .uri("lb://microservice-one"))
            .build();
    }
    
    
    
    
    
0
0
439
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Простой способ сделать это — создать customFilter с помощью GatewayFilter следующим образом:

@Component
public class MyRateLimiterFilter implements GatewayFilter {

    private final UserKeyResolver userKeyResolver;
    private final ReactiveRedisTemplate<String, String> redisTemplate;

    public MyRateLimiterFilter(ReactiveRedisTemplate<String, String> redisTemplate, UserKeyResolver userKeyResolver) {
    this.redisTemplate = redisTemplate;
    this.userKeyResolver = userKeyResolver;

    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    return userKeyResolver.resolve(exchange)
            .flatMap(key -> {
                return redisTemplate.opsForValue().increment(key)
                        .flatMap(tokens -> {
                            if (tokens == 1) return redisTemplate.expire(key, Duration.ofMillis(1000)).thenReturn(tokens);
                            return Mono.just(tokens);
                        })
                        .flatMap(tokens -> {
                            int maxReqPerSec = 1;

 // Implement rate limit based on user here, in this case "key" returns the user ID from JWT.
 // You can inject the user from anywhere like user-service or read from any properties file and set the limit !
 
        if (key.equals("consumer50tps")){
          maxReqPerSec = 50;
        }else if (key.equals("consumer100tps")){
           maxReqPerSec = 100;
        }
        
        
                            if (tokens <= maxReqPerSec) {
                                return chain.filter(exchange);
                            } else {
                                LOGGER.info("Exceeded request limit for the user: "+key + ", current number of request: "+tokens);
                                exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                                return exchange.getResponse().setComplete();
                            }
                        });
            });
    }

}

а затем добавьте этот фильтр в RouteLocator для конкретного сервиса или API и наслаждайтесь им !!

@Bean
public RouteLocator appRouteConfig(RouteLocatorBuilder builder, MyRateLimiterFilter myRateLimiter) {
return builder.routes()
                 .route(p -> p.path("/microservice-one/**")
                    .filters(f -> f
                            .filter(myRateLimiter)
                            .rewritePath("/microservice-one/(?<segment>.*)", "/${segment}")
                     )
                    .uri("lb://microservice-one"))
        .build();
}

Другие вопросы по теме