Spring Cloud Gateway 限流详解

  • 2019 年 10 月 5 日
  • 筆記

Spring Cloud Gatway内置的 RequestRateLimiterGatewayFilterFactory 提供限流的能力,基于令牌桶算法实现。目前,它内置的 RedisRateLimiter ,依赖Redis存储限流配置,以及统计数据。当然你也可以实现自己的RateLimiter,只需实现 org.springframework.cloud.gateway.filter.ratelimit.RateLimiter 接口,或者继承 org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter

漏桶算法: 想象有一个水桶,水桶以一定的速度出水(以一定速率消费请求),当水流速度过大水会溢出(访问速率超过响应速率,就直接拒绝)。 漏桶算法的两个变量: •水桶漏洞的大小:rate•最多可以存多少的水:burst 令牌桶算法: 系统按照恒定间隔向水桶里加入令牌(Token),如果桶满了的话,就不加了。每个请求来的时候,会拿走1个令牌,如果没有令牌可拿,那么就拒绝服务。 TIPS •Redis Rate Limiter的实现基于这篇文章: Stripe[1]•Spring官方引用的令牌桶算法文章: Token Bucket Algorithm[2] ,有兴趣可以看看。

写代码

1 加依赖:

<dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter-data-redis-reactive</artifactId>  </dependency>

2 写配置:

spring:    cloud:      gateway:        routes:          - id: after_route            uri: lb://user-center            predicates:              - TimeBetween=上午0:00,下午11:59            filters:              - AddRequestHeader=X-Request-Foo, Bar              - name: RequestRateLimiter                args:                  # 令牌桶每秒填充平均速率                  redis-rate-limiter.replenishRate: 1                  # 令牌桶的上限                  redis-rate-limiter.burstCapacity: 2                  # 使用SpEL表达式从Spring容器中获取Bean对象                  key-resolver: "#{@pathKeyResolver}"    redis:      host: 127.0.0.1      port: 6379

3 写代码:按照X限流,就写一个针对X的KeyResolver。

@Configuration  public class Raonfiguration {      /**       * 按照Path限流       *       * @return key       */      @Bean      public KeyResolver pathKeyResolver() {          return exchange -> Mono.just(              exchange.getRequest()                  .getPath()                  .toString()          );      }  }

4 这样,限流规则即可作用在路径上。

例如:  # 访问:http://${GATEWAY_URL}/users/1,对于这个路径,它的redis-rate-limiter.replenishRate = 1,redis-rate-limiter.burstCapacity = 2;  # 访问:http://${GATEWAY_URL}/shares/1,对这个路径,它的redis-rate-limiter.replenishRate = 1,redis-rate-limiter.burstCapacity = 2;

测试

持续高速访问某个路径,速度过快时,返回 HTTP ERROR 429

拓展

你也可以实现针对用户的限流:

@Bean  public KeyResolver userKeyResolver() {      return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));  }

针对来源IP的限流:

@Bean  public KeyResolver ipKeyResolver() {    return exchange -> Mono.just(      exchange.getRequest()      .getHeaders()      .getFirst("X-Forwarded-For")    );  }

相关文章

Spring Cloud限流详解(附源码)

References

[1] Stripe: https://stripe.com/blog/rate-limiters [2] Token Bucket Algorithm: https://en.wikipedia.org/wiki/Token_bucket