SpringCloud GateWay custom filter GatewayFilter and AbstractGatewayFactory

1. GatewayFilter

GatewayFilter is a simple interface used to define the behavior of a gateway filter. A gateway filter is a class that implements the GatewayFilter interface and can perform certain operations when a request enters the gateway or when a response leaves the gateway. Filters can be used to modify requests or responses, log, add headers, and more.

public interface GatewayFilter {

    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

A simple custom gateway filter,:

public class MyFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put("start",System.currentTimeMillis());
        return chain.filter(exchange).then(Mono.fromRunnable(new Runnable() {
            @Override
            public void run() {
                long start = exchange.getAttribute("start");
                System.out.println(exchange.getRequest().getURI() + "Execution time:" + (System.currentTimeMillis()-start));
            }
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

Configuration:

@Configuration
public class MyConfig {
    /**Configure custom filters*/
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes().route(r ->
                        r.path("/provider/**")//The path visited by the user
                                .uri("lb://service-provider")//The real server ip + port of the route
                                .filters(new MyFilter()) // Local filter
                                .id("provider_route")) // Route id
                .build();
    }
}

2. AbstractGatewayFilterFactory

AbstractGatewayFilterFactory is an abstract class used to create gateway filters more conveniently. It handles filter parameter parsing and creation, making defining filters much simpler.

public class MyCustomGatewayFilterFactory extends AbstractGatewayFilterFactory<MyCustomGatewayFilterFactory.Config> {

    public MyCustomGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config, Class<Config> configClass) {
        //Create and return filter instance here
        return (exchange, chain) -> {
            // filter logic
            return chain.filter(exchange);
        };
    }

    public static class Config {
        //Filter configuration parameters
    }
}

The following is a simple current limit implemented through the token bucket algorithm:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;

@Component
public class RateLimitByIpGatewayFilterFactory extends AbstractGatewayFilterFactory<RateLimitByIpGatewayFilterFactory.Config> {

    public RateLimitByIpGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // Get the requested IP address
            String ipAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();

            // Use simple IP-based current limiting logic, you can choose other current limiting algorithms according to actual needs
            // Here we use a simple token bucket algorithm as an example
            if (isRateLimited(ipAddress, config.getLimit())) {
                //If the current limit threshold is exceeded, return an error response
                exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                return exchange.getResponse().setComplete();
            }

            // If the current limit threshold is not exceeded, continue processing the request
            return chain.filter(exchange);
        };
    }

    private boolean isRateLimited(String ipAddress, int limit) {
        //Implement your current limiting logic here, using a simple token bucket algorithm as an example
        // You can use a library such as Google Guava RateLimiter to simplify the implementation
        // This is just a simple example, please make more complex implementations based on actual needs.
        // In a real scenario, you may need to record the access frequency to the database or distributed cache

        //A simple Map is used here to simulate the storage token bucket.
        Map<String, Long> tokenBucket = new ConcurrentHashMap<>();

        // Get the current timestamp
        long now = System.currentTimeMillis();

        // Get or initialize the token bucket
        tokenBucket.putIfAbsent(ipAddress, now);

        // Get the last access time
        long lastAccessTime = tokenBucket.get(ipAddress);

        // Calculate time interval
        long interval = now - lastAccessTime;

        // Calculate token generation rate
        double rate = 1000.0 / limit; // Assume a limit on the number of requests per second

        // Calculate the number of tokens that should be generated
        int tokensToAdd = (int) (interval / rate);

        //Update the number of tokens in the token bucket
        tokenBucket.put(ipAddress, now + tokensToAdd);

        // Check if the number of tokens exceeds the threshold
        return tokensToAdd > limit;
    }

    public static class Config {
        private int limit;

        public int getLimit() {
            return limit;
        }

        public void setLimit(int limit) {
            this.limit = limit;
        }
    }
}

Configuration file configuration current limiting threshold:

spring:
  cloud:
    gateway:
      routes:
        - id: rate_limit_route
          uri: http://example.com
          filters:
            - RateLimitByIp=1
          predicates:
            - Path=/api/**

The above configuration will limit requests under the /api/** path to 1 request per second. Please note that RateLimitByIp needs to be in the same case as the class name of RateLimitByIpGatewayFilterFactory, and the parameter 1 is used to set the current limit threshold. You Can be adjusted as needed.

  1. Fixed-capacity token bucket: There is a fixed number of tokens in the token bucket, and these tokens are added to the bucket at a fixed rate.

  2. Token Add Rate: Tokens are added to the token bucket at a constant rate (e.g. a fixed number of tokens per second).

  3. Token consumption: When a request arrives, a token needs to be obtained from the token bucket. If there are enough tokens in the token bucket, the request is allowed to be processed and one token is consumed; otherwise, the request is throttled.

  4. Smooth current limiting: Since tokens are added at a constant rate, the token bucket algorithm can achieve smooth current limiting, that is, requests are processed evenly instead of being rejected suddenly.

  5. Adapt to burst traffic: The token bucket algorithm is also adaptable to handle burst traffic, because even if the token bucket is empty for a period of time, once a token is added, it can handle new ones. ask.

  6. Good fault tolerance: Since the token bucket algorithm is time-based, it has good fault tolerance for time-sensitive applications. And since each request requires a token from the token bucket, the impact of burst requests on the system can be effectively prevented.

3. Difference

  1. Designed:

  • GatewayFilter: It is a simple interface used to define the behavior of gateway filters. The implementation of each filter needs to directly implement the methods in the GatewayFilter interface.
  • AbstractGatewayFilterFactory: is an abstract class designed to make it easier to create gateway filters with configuration parameters. By inheriting this abstract class, you can more easily handle the parsing of configuration parameters and the creation of filter instances.
  1. Usage:

  • GatewayFilter: Directly implement the GatewayFilter interface and write filter logic. This approach is suitable for simple filters that do not require configuration parameters.
  • AbstractGatewayFilterFactory: Inherit this abstract class, implement the abstract methods apply and apply(C config, Class configClass), and process the configuration there parameters and create a filter instance. This method is suitable for filters that require configuration parameters.
  1. Configuration parameters:

  • GatewayFilter: If the filter requires configuration parameters, the parameters need to be passed through other methods (such as constructors, property injection, etc.) because the GatewayFilter interface itself does not provide a direct configuration mechanism.
  • AbstractGatewayFilterFactory: Specify the type of configuration parameters through the generic parameter C and receive the configuration parameters in the apply method. This allows for more flexible handling of configuration parameters, and Spring Cloud Gateway will be responsible for binding the configuration parameters to the filter instance.