SpringCloud GateWay自定义过滤器之GatewayFilter和AbstractGatewayFactory

一、GatewayFilter

GatewayFilter 是一个简单的接口,用于定义网关过滤器的行为。一个网关过滤器就是一个实现了 GatewayFilter 接口的类,它可以执行在请求进入网关或响应离开网关时的某些操作。过滤器可以用于修改请求或响应,记录日志,添加头部信息,等等。

java 复制代码
public interface GatewayFilter {

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

}

一个简单的自定义网关过滤器,:

java 复制代码
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() + "执行耗时:" + (System.currentTimeMillis()-start));
            }
        }));
    }

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

配置:

java 复制代码
@Configuration
public class MyConfig {
    /**配置自定义过滤器*/
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes().route(r ->
                        r.path("/provider/**")//用户访问的路径
                                .uri("lb://service-provider")//路由的真实服务器ip+端口
                                .filters(new MyFilter()) // 局部过滤器
                                .id("provider_route")) // 路由id
                .build();
    }
}

二、AbstractGatewayFilterFactory

AbstractGatewayFilterFactory 是一个抽象类,用于更方便地创建网关过滤器。它处理过滤器的参数解析和创建,使得定义过滤器变得更加简单。

java 复制代码
public class MyCustomGatewayFilterFactory extends AbstractGatewayFilterFactory<MyCustomGatewayFilterFactory.Config> {

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

    @Override
    public GatewayFilter apply(Config config, Class<Config> configClass) {
        // 在这里创建并返回过滤器实例
        return (exchange, chain) -> {
            // 过滤器逻辑
            return chain.filter(exchange);
        };
    }

    public static class Config {
        // 过滤器的配置参数
    }
}

下面是一个通过令牌桶算法实现的简单限流:

java 复制代码
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) -> {
            // 获取请求的IP地址
            String ipAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();

            // 使用简单的基于IP的限流逻辑,你可以根据实际需求选择其他限流算法
            // 这里使用一个简单的令牌桶算法作为示例
            if (isRateLimited(ipAddress, config.getLimit())) {
                // 如果超过限流阈值,返回错误响应
                exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                return exchange.getResponse().setComplete();
            }

            // 如果未超过限流阈值,继续处理请求
            return chain.filter(exchange);
        };
    }

    private boolean isRateLimited(String ipAddress, int limit) {
        // 在这里实现你的限流逻辑,这里使用一个简单的令牌桶算法作为示例
        // 你可以使用库如Google Guava RateLimiter来简化实现
        // 这里只是一个简单的示例,请根据实际需求进行更复杂的实现
        // 在真实场景中,你可能需要将访问频率记录到数据库或分布式缓存中

        // 这里使用一个简单的Map模拟存储令牌桶
        Map<String, Long> tokenBucket = new ConcurrentHashMap<>();

        // 获取当前时间戳
        long now = System.currentTimeMillis();

        // 获取或初始化令牌桶
        tokenBucket.putIfAbsent(ipAddress, now);

        // 获取上次访问时间
        long lastAccessTime = tokenBucket.get(ipAddress);

        // 计算时间间隔
        long interval = now - lastAccessTime;

        // 计算令牌生成速率
        double rate = 1000.0 / limit; // 假设限制每秒请求次数

        // 计算应该生成的令牌数量
        int tokensToAdd = (int) (interval / rate);

        // 更新令牌桶中的令牌数量
        tokenBucket.put(ipAddress, now + tokensToAdd);

        // 检查令牌数量是否超过阈值
        return tokensToAdd > limit;
    }

    public static class Config {
        private int limit;

        public int getLimit() {
            return limit;
        }

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

配置文件配置限流阈值:

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

上述配置将限制 /api/** 路径下的请求每秒只能有 1 次。请注意,RateLimitByIp 需要和 RateLimitByIpGatewayFilterFactory 的类名中的大小写一致,同时参数 1 是用来设置限流的阈值,你可以根据需要调整。

  1. 固定容量的令牌桶: 令牌桶内有固定数量的令牌,这些令牌以固定的速率被添加到桶中。

  2. 令牌添加速率: 令牌以恒定的速率(例如每秒添加固定数量的令牌)被添加到令牌桶中。

  3. 令牌消耗: 当请求到达时,需要从令牌桶中获取一个令牌。如果令牌桶中有足够的令牌,则请求被允许处理,并消耗一个令牌;否则,请求被限流。

  4. 平滑限流: 由于令牌以恒定速率被添加,令牌桶算法可以实现平滑限流,即请求被均匀地处理,而不是突然被拒绝。

  5. 适应突发流量: 令牌桶算法对于处理突发流量也具有一定的适应性,因为即使令牌桶空了一段时间,一旦有令牌被添加,就可以处理新的请求。

  6. 容错性好: 由于令牌桶算法是基于时间的,因此对于时间敏感的应用来说,容错性较好。而且由于每个请求都需要从令牌桶中获取令牌,因此可以有效防止突发请求对系统的影响。

三、区别

  1. 设计用途:
  • GatewayFilter : 用于定义网关过滤器的行为,是一个简单的接口。每个过滤器的实现需要直接实现 GatewayFilter 接口中的方法。
  • AbstractGatewayFilterFactory: 是一个抽象类,旨在更方便地创建具有配置参数的网关过滤器。通过继承这个抽象类,你可以更容易地处理配置参数的解析和过滤器实例的创建。
  1. 用法:
  • GatewayFilter : 直接实现 GatewayFilter 接口,编写过滤器逻辑。这种方式适用于不需要配置参数的简单过滤器。
  • AbstractGatewayFilterFactory : 继承该抽象类,实现抽象方法 applyapply(C config, Class<C> configClass),并在其中处理配置参数并创建过滤器实例。这种方式适用于需要配置参数的过滤器。
  1. 配置参数:
  • GatewayFilter : 如果过滤器需要配置参数,需要通过其他方式(如构造函数、属性注入等)传递参数,因为 GatewayFilter 接口本身不提供直接的配置机制。
  • AbstractGatewayFilterFactory : 通过泛型参数 C 指定配置参数的类型,并在 apply 方法中接收配置参数。这使得配置参数的处理更为灵活,Spring Cloud Gateway 会负责将配置参数绑定到过滤器实例。
相关推荐
云烟成雨TD17 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-18 小时前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫18 小时前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD19 小时前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly20 小时前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly20 小时前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh20 小时前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly20 小时前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode21 小时前
Spring 依赖注入方式全景解析
java·后端·spring