spring-cloud/spring-cloud-gateway

RedisRateLimiter: Support configuration in Fluent API

Open

#2,246 opened on May 13, 2021

View on GitHub
 (2 comments) (0 reactions) (0 assignees)Java (3,204 forks)batch import
enhancementhelp wanted

Repository metrics

Stars
 (4,284 stars)
PR merge metrics
 (Avg merge 1d 21h) (18 merged PRs in 30d)

Description

Is your feature request related to a problem? Please describe. There seems to be no way to configure the RedisRateLimiter per route when using the fluent API. It is readily available if using application.yaml configuration via filter args.

Describe the solution you'd like To be honest I'm not completely sure what the best solution is. The trick here is that the RedisRateLimiter is an implementation of the RateLimiter. The filter itself is implemented via RequestRateLimiterGatewayFilterFactory. The reason this is tricky because filter configurations are easy, but this would actually need to seed the configuration for the individual ratelimiter.

Perhaps the most ideal might be a mechanism that allows the filter args to be passed through like we see with metadata.

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
     return builder.routes()
             .route("hello", p -> p.path("/api/v1/hello/**")
                     .filters(s -> s.requestRateLimiter(c -> c
                           .setRateLimiter(myRedisRateLimiter)
                           .setKeyResolver(keyResolver)
                           .filterArgs(Map.of("redis-rate-limiter.replenishRate", 60)))
                      .uri("helloService")
             ).build();
 }

The redis-rate-limiter.replenishRate could probably be a constant much like CONNECT_TIMEOUT_ATTR or RESPONSE_TIMEOUT_ATTR.

Alternatives 1 Alternatively maybe it would make sense for the configuration to occur at the specific ratelimiter implementation. That would create a less than desirable gap between the route definition, but it would provide for a better interface.

@Bean
public RedisRateLimiter redisRateLimiter() {
     RedisRateLimiter redisRateLimiter = new RedisRateLimiter(0, 0, 0);
     redisRateLimiter.configure("myRouteId", c-> c.setReplenishRate(10));
     return redisRateLimiter;
 }

Alternatives 2

A combination of the two solutions above might look something like this:

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
     return builder.routes()
             .route("hello", p -> p.path("/api/v1/hello/**")
                     .filters(s -> s.requestRateLimiter(c -> c
                           .setRateLimiter(myRedisRateLimiter.configure("hello",  c-> c.setReplenishRate(10))
                           .setKeyResolver(keyResolver)
                           .filterArgs(Map.of("redis-rate-limiter.replenishRate", 60)))
                      .uri("helloService")
             ).build();
 }

Additional context Our use case is that our routing configuration has outgrown using the application.yaml. We have several situations where routes are dynamically determined and having them hardcoded doesn't work well.

Contributor guide