Spring Boot 限流实战教程

Last Modified: 2023/08/11

概述

这是一篇 Spring Boot 项目中如何限流的实战教程,使用的库为 ratelimiter-spring-boot-starter。该库使用的限流算法为令牌桶算法,本文不打算介绍令牌桶算法,因为网上一搜一大把,说的都是大同小异,如果感兴趣请自行查阅。

1、安装依赖

<dependency>
    <groupId>net.verytools.tools</groupId>
    <artifactId>ratelimiter-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>

注:该库目前只支持 spring boot 2.x,不支持 spring boot 1.x。

2、配置 Redis

由于该库使用了 redis lua script 实现令牌桶算法,因此需要用到 Redis,配置 Redis 的方法如下:

spring.redis.host=127.0.0.1
spring.redis.port=6379

注:请根据实际情况配置 Redis 的 host 和 Port。

3、启用限流相关的 Bean

启用限流相关的 Bean 是通过 @EnableRateLimiter 注解实现的,可以将该注解放到 spring boot 项目 main 方法所在的类上。假如 main 方法所在的类为 YourAwesomeApplication,那么配置如下:

@EnableRateLimiter
@SpringBootApplication
public class YourAwesomeApplication {
  public static void main(String[] args) {
     // ...
  }
}

4、配置限流参数和限流策略

限流的参数有以下几个:

  • window:表示时间间隔,可以指定“1s”、“2m”、“2h" 等,分别表示 1秒、2分钟和2小时。window 格式为数字加单位,有效的单位包括秒、分和时。
  • windowTokens:和 window 参数配合使用,表示每隔 window 参数指定的时间间隔往令牌桶补充的令牌数量,如果令牌桶满了则不会补充。
  • burstCapacity:表示令牌桶的容量,开始的时候桶是满的。
  • requestedTokens:表示每个用户请求消耗的令牌数量,默认值为 1。

限流策略分为三种,可以任选其一,或者搭配使用。

  • 全局限流
  • 限制特定前缀开头的 api
  • 限制具体的 api

4.1、全局限流

全局限流表示所有的 api 都会被限流,这里的 api 指的是 controller 中的 handler method。配置方法很简单,只需要在 application.properties 文件中加入以下配置即可:

rate-limiter.config.window=1s
rate-limiter.config.windowTokens=3
rate-limiter.config.burstCapacity=20
rate-limiter.config.requestedTokens = 1

上面的配置表示的是每隔 1s 往令牌桶中加入 3 个令牌,桶的最大容量为 20,每个请求会消耗一个令牌。如果不想开启全局配置,直接不配置就可以了。只要配置了任何一个参数就代表了开启全局限流。

4.2、限制特定前缀开头的 api

如果只想限制某些特定前缀的 api,例如只想限流 ‘/api/**’ 开头的路由,可以使用 Java Config,提供一个 PathPatternRateLimitRuleConfig 类型的 Bean 即可。

@Configuration
public class RateLimiterConfig {

  @Bean
  public PathPatternRateLimitRuleConfig pathPatternRateLimitRuleConfig() {
    RedisRateLimiterProperties.Config config = new RedisRateLimiterProperties.Config();
    config.setBurstCapacity(3);
    config.setRequestedTokens(1);
    config.setWindow("10s");
    config.setWindowTokens(2);
    
    return new PathPatternRateLimitRuleConfig()
            .rule(config, "/api/**");
  }
  
  @Bean
  public KeyResolver keyResolver() {
    // keyResolver 是配置限流对象的,后面会单独说明。
  }

}

4.3、限制具体的 api

限制具体的某个路由可以使用 @RateLimit 注解。

@RateLimit(burstCapacity = 1, window = "1s", windowTokens = 1)
@GetMapping("/api/awesome")
public String awesome() {
    return "awesome";
}

5、配置限流对象

配置限流参数还不够,我们需要指定限流对象,限流对象可以是用户的 ip,id 或者 access token 等。配置限流对象是通过 KeyResolver 实现的,所以可以限流对象取决于你的实现,请发挥你的想象。下面提供一个根据 ip 来限流的实现。

@Configuration
public class RateLimiterConfig {
    
  @Bean
  public KeyResolver keyResolver() {
      return new KeyResolver() {
          @Override
          public String resolve(HttpServletRequest req) {
            return req.getRemoteAddr();
          }
      };
  }

}

注意:使用 req.getRemoteAddr() 来获取用户 ip 是不靠谱的,请根据实际情况获取用户的 ip,如果实在不知道怎么获取用户的真实 ip,可以使用 hutools 中的 ServletUtils.getClientIp

有问题吗?点此反馈!

温馨提示:反馈需要登录