Spring Boot 限流实战教程
概述
这是一篇 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。
温馨提示:反馈需要登录