限流模块
简介
continew-starter-ratelimiter 是 ContiNew Starter 提供的基于 AOP 和 Redisson 内置分布式限流器(采用令牌桶算法)整合实现的分布式限流模块,提供开箱即用的分布式限流解决方案。
主要特性
- 基于 Redisson 的分布式限流器实现(采用令牌桶算法),以恒定速率生成令牌,每个请求需要获取令牌才能被处理
- 支持灵活的多维度限流场景:
- IP 限流(限制单个客户端访问频率)
- 单节点限流(保障单实例服务稳定性)
- 全局限流(集群环境下的全局速率控制)
- 提供 SpEL 表达式支持,支持动态解析请求参数进行限流 key 配置
- 示例:
@RateLimiter(key = "#userId")
- 示例:
- 支持限流器链配置,可组合多个限流策略进行多级流量控制
快速开始
引入依赖
<dependency> <groupId>top.continew.starter</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 接口
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.starter:continew-starter-cache-redisson | 缓存模块 |
| top.continew.starter:continew-starter-web | Web 模块 |
参考资料
1.Redisson 官网-RateLimiter:https://redisson.pro/docs/data-and-services/objects/#ratelimiter