限流模块
简介
continew-starter-ratelimiter
是 ContiNew Starter 提供的基于 AOP 和 Redisson 内置分布式限流器(采用令牌桶算法)整合实现的分布式限流模块,提供开箱即用的分布式限流解决方案。
主要特性
- 基于 Redisson 的分布式限流器实现(采用令牌桶算法),以恒定速率生成令牌,每个请求需要获取令牌才能被处理
- 支持灵活的多维度限流场景:
- IP 限流(限制单个客户端访问频率)
- 单节点限流(保障单实例服务稳定性)
- 全局限流(集群环境下的全局速率控制)
- 提供 SpEL 表达式支持,支持动态解析请求参数进行限流 key 配置
- 示例:
@RateLimiter(key = "#userId")
- 示例:
- 支持限流器链配置,可组合多个限流策略进行多级流量控制
使用步骤
引入依赖
<dependency>
<groupId>top.continew</groupId>
<artifactId>continew-starter-ratelimiter</artifactId>
</dependency>
全局配置
配置详情请查看:top.continew.starter.ratelimiter.autoconfigure.RateLimiterProperties
。
--- ### 限流器配置
continew-starter:
rate-limiter:
# 是否启用(默认:已启用)
enabled: true
# 限流统一 Key 前缀(默认:RateLimiter)
key-prefix: RateLimiter
使用 @RateLimiter 注解
在需要限流处理的接口上添加 top.continew.starter.ratelimiter.annotation.@RateLimiter
注解,支持通过 @RateLimiters
注解组合多个限流策略。
下方以预防短信炸弹的限流策略为例:
- 同一号码同一模板,1分钟2条,1小时8条,24小时20条
- 同一号码所有模板 24 小时 100 条
- 同一 IP 每分钟限制发送 30 条
温馨提示
短信炸弹不能单凭限流器进行预防,还要结合行为验证码等其他措施,下方仅示例了短信炸弹的限流策略。
@Operation(summary = "获取短信验证码", description = "发送验证码到指定手机号")
@GetMapping("/sms")
@RateLimiters({
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "MIN", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 2, interval = 1, unit = TimeUnit.MINUTES, message = "获取验证码操作太频繁,请稍后再试"),
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "HOUR", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 8, interval = 1, unit = TimeUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX + "DAY'", key = "#phone + ':' + T(cn.hutool.extra.spring.SpringUtil).getProperty('captcha.sms.templateId')", rate = 20, interval = 24, unit = TimeUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 100, interval = 24, unit = TimeUnit.HOURS, message = "获取验证码操作太频繁,请稍后再试"),
@RateLimiter(name = CacheConstants.CAPTCHA_KEY_PREFIX, key = "#phone", rate = 30, interval = 1, unit = TimeUnit.MINUTES, type = LimitType.IP, message = "获取验证码操作太频繁,请稍后再试")
})
public R getSmsCaptcha(@NotBlank(message = "手机号不能为空") @Mobile String phone) {
// ...略
}
@RateLimiter 注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
/**
* 类型(DEFAULT:全局限流;IP:IP 全局限流;CLUSTER:集群模式单实例限流)
*/
LimitType type() default LimitType.DEFAULT;
/**
* 名称
*/
String name() default "";
/**
* 键(支持 Spring EL 表达式)
*/
String key() default "";
/**
* 速率(指定时间间隔产生的令牌数)
*/
int rate() default Integer.MAX_VALUE;
/**
* 速率间隔(时间间隔)
*/
int interval() default 0;
/**
* 速率间隔时间单位(默认:毫秒)
*/
TimeUnit unit() default TimeUnit.MILLISECONDS;
/**
* 提示信息
*/
String message() default "操作过于频繁,请稍后再试";
}
RateLimiterNameGenerator 接口
top.continew.starter.ratelimiter.generator.RateLimiterNameGenerator
是限流器名称生成器接口,当 @RateLimiter
注解的 name
属性为空时用于生成限流器名称。
public interface RateLimiterNameGenerator {
/**
* Generate a rate limiter name for the given method and its parameters.
*
* @param target the target instance
* @param method the method being called
* @param args the method parameters (with any var-args expanded)
* @return a generated rate limiter name
*/
String generate(Object target, Method method, Object... args);
}
默认已经提供了 DefaultRateLimiterNameGenerator
实现(根据方法名、方法参数等生成限流器名称),如果不满足你的需要,可自行重写 RateLimiterNameGenerator
接口来实现自定义限流器名称生成器,然后在 Spring 容器中提供 RateLimiterNameGenerator
Bean 即可。
@Configuration
public class GlobalConfiguration {
/**
* 限流器名称生成器
*/
@Bean
public RateLimiterNameGenerator nameGenerator() {
return new MyRateLimiterNameGenerator();
}
}
核心依赖
依赖 | 描述 |
---|---|
top.continew:continew-starter-cache-redisson | 缓存模块 |
top.continew:continew-starter-web | Web 模块 |
参考资料
1.Redisson 官网-RateLimiter:https://redisson.pro/docs/data-and-services/objects/#ratelimiter