Spring Cloud Gateway限流实战

https://www.jianshu.com/p/09d20f7df004

本篇概览

  • 本文是《Spring Cloud Gateway实战》系列的第八篇,经过前面的学习,咱们对过滤器已了解得差不多,今天来补全过滤器的最后一个版块:限流(RequestRateLimiter )
  • 默认的限流器是基于redis实现的,限流算法是大家熟悉的令牌桶( Token Bucket Algorithm),关于令牌捅的原理就不在此展开了,聪明的您看一眼下图应该就懂了:装令牌的桶容量有限,例如最多20个,令牌进入桶的速度恒定(注意,这里是和漏桶算法的区别),例如每秒10个,底部每个请求能拿到令牌才会被处理:

Spring Cloud Gateway限流实战_第1张图片

Spring Cloud Gateway限流实战

RequestRateLimiter基本套路

  • 使用RequestRateLimiter过滤器的步骤非常简单:
  1. 准备可用的redis
  2. maven或者gradle中添加依赖org.springframework.boot:spring-boot-starter-data-redis-reactive
  3. 确定按照什么维度限流,例如按照请求中的username参数限流,这是通过编写KeyResolver接口的实现来完成的
  4. 配置application.yml文件,添加过滤器
  • 以上就是使用RequestRateLimiter过滤器的套路了,简单么?接下来,咱们先编码再验证

源码下载

  • 本篇实战中的完整源码可在GitHub下载到,地址和链接信息如下表所示( https://github.com/zq2599/blog_demos ):

Spring Cloud Gateway限流实战_第2张图片

  • 这个git项目中有多个文件夹,本篇的源码在spring-cloud-tutorials文件夹下,如下图红框所示:

Spring Cloud Gateway限流实战_第3张图片

Spring Cloud Gateway限流实战

  • spring-cloud-tutorials文件夹下有多个子工程,本篇的代码是gateway-requestratelimiter,如下图红框所示:

Spring Cloud Gateway限流实战_第4张图片

Spring Cloud Gateway限流实战

准备工作

  • 为了更好的演示Gateway的效果,在服务提供者provider-hello的代码(Hello.java)中新增一个web接口,可以接受一个入参:

@GetMapping("/userinfo")
    public String userInfo(@RequestParam("username") String username) {
        return Constants.HELLO_PREFIX + " " + username + ", " + dateStr();
    }
  • 后面的测试咱们就用上述接口;

编码

  • 在父工程spring-cloud-tutorials之下新增子工程gateway-requestratelimiter,其pom.xml内容如下,重点是org.springframework.boot:spring-boot-starter-data-redis-reactive:



    
        spring-cloud-tutorials
        com.bolingcavalry
        1.0-SNAPSHOT
    
    4.0.0

    gateway-requestratelimiter

    
        
            com.bolingcavalry
            common
            ${project.version}
        

        
            org.springframework.cloud
            spring-cloud-starter-gateway
        

        
            org.springframework.boot
            spring-boot-starter-data-redis-reactive
        
    

  • 配置文件application.yml,请注意RequestRateLimiter的几个参数,已经用中文添加了详细的注释:

server:
  #服务端口
  port: 8081
spring:
  application:
    name: circuitbreaker-gateway
  # redis配置
  redis:
    host: 192.168.50.43
    port: 6379

  cloud:
    gateway:
      routes:
        - id: path_route
          uri: http://127.0.0.1:8082
          predicates:
            - Path=/hello/**
          filters:
            - name: RequestRateLimiter
              args:
                # 令牌入桶的速度为每秒100个,相当于QPS
                redis-rate-limiter.replenishRate: 100
                # 桶内能装200个令牌,相当于峰值,要注意的是:第一秒从桶内能去200个,但是第二秒只能取到100个了,因为入桶速度是每秒100个
                redis-rate-limiter.burstCapacity: 200
                # 每个请求需要的令牌数
                redis-rate-limiter.requestedTokens: 1
  • 指定限流维度的代码CustomizeConfig.java,这里是根据请求参数username的值来限流的,假设真实请求中一半请求的username的等于Tom,另一半的username的等于Jerry,按照application.yml的配置,Tom的请求QPS为10,Jerry的QPS也是10:

package com.bolingcavalry.gateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.Objects;

@Configuration
public class CustomizeConfig {
    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
    }
}
  • 毫无营养的启动类RequestRateLimiterApplication.java:

package com.bolingcavalry.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RequestRateLimiterApplication {
    public static void main(String[] args) {
        SpringApplication.run(RequestRateLimiterApplication.class,args);
    }
}
  • 代码写完了,接下来开始验证;

验证(桶容量等于入桶速度)

  • 首先验证的是桶容量等于入桶速度时的效果,请修改gateway-requestratelimiter应用的application.yml中文件,使得redis-rate-limiter.replenishRate和redis-rate-limiter.burstCapacity的值都等于100,也就是说桶的大小等于100,每秒放入的令牌数也是100
  • 确保redis已经启动,并且与application.yml中的配置保持一直
  • 启动nacos(provider-hello依赖)
  • 启动服务提供者provider-hello
  • 启动gateway-requestratelimiter
  • 为了模拟web请求,我这里使用了Apache Benchmark,windows版本的下载地址:https://www.apachelounge.com/download/VS16/binaries/httpd-2.4.48-win64-VS16.zip
  • 上述文件下载解压后即可使用,在控制台进入Apache24\bin后执行以下命令,意思是向指定地址发送10000个请求,并发数为2:

ab -n 10000  -c 2 http://localhost:8081/hello/userinfo?username=Tom
  • 控制台输出如下,可见不到八秒的时间,只成功了800个,证明限流符合预期:

Spring Cloud Gateway限流实战_第5张图片

Spring Cloud Gateway限流实战

验证(桶容量大于入桶速度)

  • 接下来试试桶容量大于入桶速度时的限流效果,这对于我们控制峰值响应有很重要的参考价值
  • 请修改gateway-requestratelimiter应用的application.yml中文件,redis-rate-limiter.replenishRate维持 100 不变,但是redis-rate-limiter.burstCapacity改成 200 ,也就是说每秒放入的令牌数还是100,但桶的容量翻倍了
  • 重启应用gateway-requestratelimiter
  • 再次执行以下命令,意思是向指定地址发送10000个请求,并发数为2:

ab -n 10000  -c 2 http://localhost:8081/hello/userinfo?username=Tom
  • 测试结果如下图,可见符合预期,可以将桶内令牌全部用掉,以支撑峰值超过QPS的场景:

Spring Cloud Gateway限流实战_第6张图片

Spring Cloud Gateway限流实战



作者:Java弟中弟
链接:https://www.jianshu.com/p/09d20f7df004
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(springcloud,java)