• SpringCloud - 流量控制(一):基于RequestRateLimiterGatewayFilterFactory的限流


    写在前面

    常用的更平滑的限流算法有两种:漏桶算法和令牌桶算法.
    两种限流的算法确有其独到之处,其他实现比如滑动时间窗或者三色速率标记法,其实是“漏桶”与“令牌桶”的变种。要么将“漏桶”容积换成了单位时间,要么是按规则将请求标记颜色进行处理,底层还是“令牌”的思想。

    漏桶算法和令牌桶算法的区别

    A. 漏桶算法:水(请求)以任意的速度先进入到漏桶里,桶以固定的速度出水,当水流入桶的速度大于水流出的速度,就会直接溢出(拒绝服务),可以看出漏桶算法能强行限制数据的传输速率。
    入桶:以任意速率往桶中放入水滴。
    出桶:以固定速率从桶中流出水滴。
    在这里插入图片描述

    B. 令牌桶算法:系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入令牌,如果桶已经满了,令牌就溢出了;如果桶未满,令牌可以累加。当有新的请求时,会拿走一个TOKEN,如果没有TOKEN可以使用了,就会出现阻塞或者拒绝服务。
    入桶:以固定速率往桶中放入水滴。
    流出:以任意速率从桶中流出水滴。
    在这里插入图片描述
    令牌桶算法:用于保护服务端,主要用来对调用者频率进行限流,目的是不让服务端被压垮。如果服务端本身有处理能力的时候,如果流量突发(实际消费能力强于配置的流量限制=桶大小),那么实际处理速率可以超过配置的限制(桶大小)。
    漏桶算法:用于保护调用方,也就是保护所调用的系统。如果调用的第三方系统本身没有保护机制,或者有流量限制的时候,调用速度不能超过他的限制,由于我们不能更改第三方系统,所以只有在主调方控制。这时即使流量突发也必须舍弃,因为消费能力是第三方决定的。

    Spring Cloud Gateway的限流

    Spring Cloud Gateway 官方提供了 RequestRateLimiterGatewayFilterFactory 过滤器工厂,使用 REDIS 和 LUA 脚本实现了令牌桶的方式。限流规则由KeyResolver接口的具体实现类来决定,常用的规则有:IP、URL、参数等等来进行限流。
    官方文档

    如何使用

    ① 添加依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    ② 修改/增加配置
    在GATEWAY的.YML文件中进行如下配置(主要是REDIS和RequestRateLimiter):
    spring:
      redis:
        host: localhost
        port: 6379
        password: 
      cloud:
        gateway:
          discovery:
            locator:
              lowerCaseServiceId: true
              enabled: true
          routes:
            # 系统模块
            - id: servicex-system
              uri: lb://servicex-system
              predicates:
                - Path=/system/**
              filters:
                - StripPrefix=1
                # 稳定速率是通过把replenishRate(补充令牌速度)和burstCapacity(令牌桶容量)设置为相同的值来实现的;
                # 可通过设置burstCapacity高于replenishRate来允许临时突发流量。在这种情况下,限流器需要在两次突发请求之间留出一段时间(根据replenishRate),因为连续两次突发将导致请求丢失(HTTP 429 - Too Many Requests).
                - name: RequestRateLimiter
                  args:
                    redis-rate-limiter.replenishRate: 1     # 令牌桶每秒填充速率, 指的是允许用户每秒执行多少请求,不丢弃任何请求;
                    redis-rate-limiter.burstCapacity: 5     # 令牌桶总容量, 指的是用户在一秒钟内允许执行的最大请求数,也就是令牌桶可以保存的令牌数, 如果将此值设置为零将阻止所有请求;
                    redis-rate-limiter.requestedTokens: 1   # 指的是每个请求消耗多少个令牌, 默认是1.
                    key-resolver: "#{@pathKeyResolver}"     # 指的是限流的时候以什么维度来判断,使用SpEL表达式按名称引用BEAN(REDIS中限流相关的KEY和此处的配置相关).
            # 文件服务
            - id: servicex-file
              uri: lb://servicex-file
              predicates:
                - Path=/file/**
              filters:
                - StripPrefix=1
    # 不校验白名单
    ignore:
      whites:
        - /auth/logout
        - /auth/login
        - /*/v2/api-docs
        - /csrf
    
    • 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
    • 42
    ③ 定义限流维度
    1. 多个限流维度, 需要使用@Primary指定;
    2. 在配置文件中指定的限流维度的名字来自该文件,key-resolver: "#{@pathKeyResolver}"@Configuration
    public class KeyResolverConfiguration {
    
        @Primary
        @Bean
        public KeyResolver pathKeyResolver() {
            return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
        }
    
        @Bean
        public KeyResolver parameterKeyResolver() {
            return exchange -> Mono.just(exchange.getRequest().getHeaders().get("username").get(0));
        }
    
        @Bean
        public KeyResolver ipKeyResolver() {
            return exchange -> Mono.just(exchange.getRequest().getURI().getHost());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    ④ 查看数据

    在REDIS的DB0数据库中会生成两个KEY,它们的TTL都很短,分别为:
    request_rate_limiter.{限流维度名字}.tokens
    request_rate_limiter.{限流维度名字}.timestamp

    ⑤ 使用SWAGGER测试

    在这里插入图片描述

    如何封装429状态码?

    问题:我们的代码如何捕获429状态码并进行封装呢?

  • 相关阅读:
    【CC3200AI 实验教程11】疯壳·AI语音人脸识别(会议记录仪/人脸打卡机)-AI语音系统架构
    【Linux】《Linux命令行与shell脚本编程大全 (第4版) 》笔记-Chapter22-gawk 进阶
    并发工具类库使用的常见问题
    服务器远程管理
    CesiumJS 源码杂谈 - 从光到 Uniform
    重生奇迹MU上武器加13有加成吗?
    FreeMarker快速入门详解
    PromQL基础语法(上)-数据类型、时序选择器、二元操作符【prometheus查询语句】
    基于减法优化SABO优化ELM(SABO-ELM)负荷预测(Matlab代码实现)
    Android-Jetpack Compose的简单运用
  • 原文地址:https://blog.csdn.net/goodjava2007/article/details/125859107