GatewayFilter
是一个简单的接口,用于定义网关过滤器的行为。一个网关过滤器就是一个实现了 GatewayFilter
接口的类,它可以执行在请求进入网关或响应离开网关时的某些操作。过滤器可以用于修改请求或响应,记录日志,添加头部信息,等等。
- public interface GatewayFilter {
-
- Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain); -
- }
一个简单的自定义网关过滤器,:
- public class MyFilter implements GatewayFilter, Ordered {
- @Override
- public Mono
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;
- }
- }
配置:
- @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
是一个抽象类,用于更方便地创建网关过滤器。它处理过滤器的参数解析和创建,使得定义过滤器变得更加简单。
- public class MyCustomGatewayFilterFactory extends AbstractGatewayFilterFactory
{ -
- public MyCustomGatewayFilterFactory() {
- super(Config.class);
- }
-
- @Override
- public GatewayFilter apply(Config config, Class
configClass) { - // 在这里创建并返回过滤器实例
- return (exchange, chain) -> {
- // 过滤器逻辑
- return chain.filter(exchange);
- };
- }
-
- public static class Config {
- // 过滤器的配置参数
- }
- }
下面是一个通过令牌桶算法实现的简单限流:
- 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
{ -
- 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
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;
- }
- }
- }
配置文件配置限流阈值:
- spring:
- cloud:
- gateway:
- routes:
- - id: rate_limit_route
- uri: http://example.com
- filters:
- - RateLimitByIp=1
- predicates:
- - Path=/api/**
上述配置将限制 /api/**
路径下的请求每秒只能有 1 次。请注意,RateLimitByIp
需要和 RateLimitByIpGatewayFilterFactory
的类名中的大小写一致,同时参数 1
是用来设置限流的阈值,你可以根据需要调整。
固定容量的令牌桶: 令牌桶内有固定数量的令牌,这些令牌以固定的速率被添加到桶中。
令牌添加速率: 令牌以恒定的速率(例如每秒添加固定数量的令牌)被添加到令牌桶中。
令牌消耗: 当请求到达时,需要从令牌桶中获取一个令牌。如果令牌桶中有足够的令牌,则请求被允许处理,并消耗一个令牌;否则,请求被限流。
平滑限流: 由于令牌以恒定速率被添加,令牌桶算法可以实现平滑限流,即请求被均匀地处理,而不是突然被拒绝。
适应突发流量: 令牌桶算法对于处理突发流量也具有一定的适应性,因为即使令牌桶空了一段时间,一旦有令牌被添加,就可以处理新的请求。
容错性好: 由于令牌桶算法是基于时间的,因此对于时间敏感的应用来说,容错性较好。而且由于每个请求都需要从令牌桶中获取令牌,因此可以有效防止突发请求对系统的影响。
设计用途:
GatewayFilter
接口中的方法。用法:
GatewayFilter
接口,编写过滤器逻辑。这种方式适用于不需要配置参数的简单过滤器。apply
和 apply(C config, Class configClass)
,并在其中处理配置参数并创建过滤器实例。这种方式适用于需要配置参数的过滤器。配置参数:
GatewayFilter
接口本身不提供直接的配置机制。C
指定配置参数的类型,并在 apply
方法中接收配置参数。这使得配置参数的处理更为灵活,Spring Cloud Gateway 会负责将配置参数绑定到过滤器实例。