Java中可通过自定义@RateLimit注解+Spring AOP+Redis Lua脚本实现接口限流:解析注解参数生成唯一key,执行Lua原子脚本判断是否放行,超限返回429,支持SpEL动态key和fallback降级。

在 Java 中,可以通过自定义注解 + AOP(如 Spring AOP 或 AspectJ)+ 缓存(如 Redis 或本地 Caffeine)来实现基于注解的接口访问频率控制(Rate Limit)。核心思路是:在方法执行前拦截带限流注解的方法,检查当前请求是否超过设定阈值,超限则抛出异常或返回错误响应。
1. 定义自定义限流注解
创建一个运行时保留、可作用于方法的注解,用于声明限流规则:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
int value() default 10; // 每时间窗口内最大请求数
int period() default 60; // 时间窗口秒数,默认 60 秒
String key() default ""; // 限流 Key 前缀,支持 SpEL 表达式(如 #user.id)
String fallback() default ""; // 可选:指定降级方法名(需同类中 public 方法)
}2. 实现限流切面(Spring AOP)
使用 @Aspect 拦截标注了 @RateLimit 的方法,解析参数并生成唯一限流 Key,再调用限流器判断是否放行:
关键点:
立即学习“Java免费学习笔记(深入)”;
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 用
JoinPoint获取方法参数,配合 SpEL 解析key()字符串(推荐用ExpressionParser+StandardEvaluationContext) - 组合业务标识(如用户 ID、IP、接口路径)生成分布式 Key,例如:
"rate:limit:api:/order/create:uid_123" - 使用 Redis 实现分布式限流(推荐 Lua 脚本保证原子性),或本地缓存(适合单机、低一致性要求场景)
- 若拒绝请求,可抛出
RuntimeException(如RateLimitException),由全局异常处理器统一返回 429 Too Many Requests
3. Redis + Lua 实现原子限流逻辑
将“获取当前计数 → 判断是否超限 → 未超限则递增并设置过期”封装为 Lua 脚本,避免多步操作并发问题:
-- KEYS[1]: 限流 key
-- ARGV[1]: 窗口大小(秒)
-- ARGV[2]: 最大请求数
local current = redis.call("INCR", KEYS[1])
if current == 1 then
redis.call("EXPIRE", KEYS[1], ARGV[1])
end
if current > tonumber(ARGV[2]) then
return 0
else
return 1
endJava 中通过 RedisTemplate.execute() 执行该脚本,返回 1 表示放行,0 表示被限流。
4. 支持灵活 Key 生成与降级(可选增强)
提升实用性可补充:
-
Key 表达式支持:用 Spring 的
SpelExpressionParser解析@RateLimit(key = "#request.header['X-User-ID'] ?: 'anonymous'") -
降级方法调用:若配置了
fallback,通过反射调用同对象内对应方法(注意签名兼容性) - 多级限流:结合用户级别(VIP/普通)、IP、接口维度叠加判断(如 VIP 用户每分钟 100 次,普通用户 10 次)
不复杂但容易忽略细节:注意注解生效范围(确保目标方法被 Spring 管理且非 private/final)、Redis 连接稳定性、Lua 脚本调试、以及高并发下本地缓存可能失效导致误放行。生产建议优先用 Redis + Lua 方案。

















