如何实现 Spring Cloud Gateway 速率限制,对于相同的 API/URL 对于不同的用户具有不同的速率限制值?

问题描述 投票:0回答:1

我有一些带有 Spring Cloud 的微服务,现在我必须根据用户名对云网关上的 API 实现速率限制。

在 KeyResolver 的帮助下,我从请求者 JWT 令牌中提取用户 并为所有用户实现了具有相同速率限制值(consumer50tps)的 RedisRateLimiter,如下所示,工作正常。

现在的要求是我必须根据用户订阅设置速率限制值,而对于相同的 api/url ,不同的用户会获得不同的限制值(consumer50tps 或consumer100tps)。

当前代码对于所有用户的单个限制值(消费者 50tps)运行良好。

    @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();
    }
    
    
    
    
    
spring spring-cloud-gateway rate-limiting
1个回答
0
投票

我们可以在 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上并享受!!

@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();
}
© www.soinside.com 2019 - 2024. All rights reserved.