概述
限流:网关上有大量请求,对指定服务进行限流,可以很大程度上提高服务的可用性与稳定性,限流的目的是通过对并发访问/请求进行限速,或对一个时间窗口内的请求进行限速来保护系统。一旦达到限制速率则可以拒绝服务、排队或等待、降级。
在Spring Cloud Gateway中,有 Filter过滤器,因此可以在 pre 类型的Filter中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了 RequestRateLimiterGatewayFilterFactory 这个类,适用在Redis内的通过执行Lua脚本实现了令牌桶的方式。具体实现逻辑在 RequestRateLimiterGatewayFilterFactory 类中,lua脚本在如下图所示的文件夹中:

 限流配置
应一小伙伴要求, 将demo升级到了最新版本
- spring-cloud-dependencies: Hoxton.SR3
 
- spring-boot-dependencies: 2.2.6.RELEASE
 
代码放置在 github.
- 修改配置文件
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | server:   port: 2000 spring:   application:     name: idc-gateway2   redis:     host: localhost     port: 6379     timeout: 6000ms       jedis:       pool:         max-active: 1000           max-wait: -1ms               max-idle: 10               min-idle: 5          cloud:     consul:       host: localhost       port: 8500     gateway:       discovery:         locator:           enabled: true        routes:         - id: provider1           uri: lb://idc-provider1           predicates:             - Path=/p1/**           filters:             - StripPrefix=1             - name: RequestRateLimiter               args:                 key-resolver: '#{@ipKeyResolver}'                 redis-rate-limiter.replenishRate: 1                 redis-rate-limiter.burstCapacity: 3         - id: provider2           uri: lb://idc-provider2           predicates:             - Path=/p2/**           filters:             - StripPrefix=1
   | 
 
在上面的配置文件,指定程序的端口为2000,配置了 redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:
- burstCapacity,令牌桶总容量。
 
- replenishRate,令牌桶每秒填充平均速率。
 
- key-resolver,用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
 
- 限流配置
 
这里根据用户ID限流,请求路径中必须携带userId参数
1 2 3 4
   | @Bean KeyResolver userKeyResolver() {   return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); }
   | 
 
KeyResolver需要实现resolve方法,比如根据userid进行限流,则需要用userid去判断。实现完KeyResolver之后,需要将这个类的Bean注册到Ioc容器中。
如果需要根据IP限流,定义的获取限流Key的bean为:
1 2 3 4 5
   | @Primary @Bean KeyResolver ipKeyResolver() {   return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); }
   | 
 
通过exchange对象可以获取到请求信息,这边用了HostName,如果你想根据用户来做限流的话这边可以获取当前请求的用户ID或者用户名就可以了,比如:
如果需要根据接口的URI进行限流,则需要获取请求地址的uri作为限流key,定义的Bean对象为:
1 2 3 4
   | @Bean KeyResolver apiKeyResolver() {   return exchange -> Mono.just(exchange.getRequest().getPath().value()); }
   | 
 
- 完整的配置文件:
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | @Component public class RateLimitConfig {     @Bean     KeyResolver userKeyResolver() {         return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));     }
      @Primary     @Bean     KeyResolver ipKeyResolver() {         return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());     }
      @Bean     KeyResolver apiKeyResolver() {         return exchange -> Mono.just(exchange.getRequest().getPath().value());     } }
   | 
 
 限流演练
这里我们用 postman 进行测试




 基于系统负载的动态限流
在实际工作中,我们可能还需要根据网络连接数、网络流量、CPU 或内存负载等来进行动态限流。在这里我们以 CPU 为栗子。
我们需要借助 Spring Boot Actuator 提供的 Metrics 能力进行实现基于 CPU 的限流 —— 当 CPU 使用率高于某个阈值就开启限流,否则不开启限流。
我们在项目中引入 Actuator 的依赖坐标
1 2 3 4
   | <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
   | 
 
然后基于过滤器和metrics指标判断是否需要限流。
 最后
本文到此结束,感谢大家的阅读。欢迎关注公众号【当我遇上你】