自己做的微服务网站今天终于算是上线了(内部测试),虽然是内部测试,但在上线之前还是考虑做了一下gateway网关的限流,担心群众里面有坏人🐍

利用RequestRateLimiter实现简单限流

RequestRateLimiter是gateway自带的 过滤器。

RequestRateLimiter 过滤器可以基于令牌桶算法实现请求限流。其具体原理如下:

  1. 创建令牌桶:在 RequestRateLimiter 过滤器初始化时,会读取配置参数创建对应的令牌桶。

  2. 生成令牌:当新的请求到达 RequestRateLimiter 过滤器时,它会检查令牌桶中是否有足够的令牌。如果有,则从令牌桶中移除一个令牌;否则请求被拒绝。

  3. 限制连接数:在超出最大连接数之后会在队列里等待。

  4. 计时更新令牌桶大小:RequestRateLimiter 过滤器会定期计时更新令牌桶的大小,确保让进入的请求有机会得到执行,同时防止恶意攻击或者系统故障导致服务器负载过重。

在使用 RequestRateLimiter 过滤器时,我们可以通过设置参数 replenishRateburstCapacity 来控制令牌桶的填充速率和容量,然后将其与特定的路由或服务进行绑定,以实现对特定接口的限流效果。

资料来源——知乎用户:苏格拉没有底

实现流程

  1. 引入redis依赖并配置连接信息
    RequestRateLimiter 一般会依赖 ReactiveStringRedisTemplate, 而它又依赖到 ReactiveRedisConnectionFactory 的实现类LettuceConnectionFactory

    1
    2
    3
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.2.5.RELEASE</version>
  2. 实现限流策略

    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
    /**
    * @Author 林峰
    * @Date 2024/4/6 10:22
    * @Version 1.0
    */
    @Component
    @Slf4j
    public class RequestRateLimiterResolver {

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

    @Primary
    @Bean
    public KeyResolver pathKeyResolver(){
    // 根据请求路径限流
    return exchange -> Mono.just(
    exchange.getRequest()
    .getPath()
    .toString()
    );
    }
    }

    过滤器接受一个可选的keyResolver参数和特定于速率限制器的参数。该参数的作用就是用来根据你设定的规则生成key,比如在redis中使用什么key。

  3. 配置限流

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - id: subject # 路由ID 刷题模块
    uri: lb://al-club-subject
    predicates:
    - Path=/subject/**
    filters:
    - StripPrefix=1
    - name: RequestRateLimiter
    args:
    # 令牌桶填充速率
    redis-rate-limiter.replenishRate: 50
    # 令牌桶上限
    redis-rate-limiter.burstCapacity: 50
    # 获取Bean对象
    key-resolver: "#{@pathKeyResolver}"

    “#{@pathKeyResolver}”是一个SpEL表达式,它引用了一个名为userKeyResolver的bean,也就是我们刚刚定义的KeyResolver,这样就可以实现一个简单的限流。

测试结果

修改nacos上的配置,改小一点,方便演示

image-20240406133127520

进行一次小小的压测(软件是apifox)

image-20240406133252695

结果

image-20240406133445017

可以看到限流成功,失败请求的错误码为429,代表Too many Requests