微服务轻量限流器核心是注解+反射+动态限流:定义支持多维度配置的ratelimit注解,通过aop反射解析上下文,统一对接redis分布式限流引擎,并协同防重入保障幂等。

直接用注解标记方法 + 反射读取规则 + 运行时动态限流,是微服务中实现轻量、可复用限流器的核心路径。关键不在“手写”,而在“通用”——即不绑定具体框架、不耦合业务逻辑、支持多服务共用同一套限流策略与状态管理。
定义清晰、可扩展的限流注解
注解是配置入口,必须覆盖真实场景中的常见维度:
-
时间窗口:支持秒/分/小时级,如
@RateLimit(window = 60, unit = TimeUnit.SECONDS) -
最大请求数:如
maxRequests = 100,配合窗口构成 QPS/TPS 约束 -
限流键生成策略:默认用「类名+方法名」,但需支持 SpEL 表达式,例如
key = "#user.id + ':' + #order.type",便于按用户、租户、渠道等维度隔离 - 拒绝行为:是否抛异常、返回固定 JSON、还是走降级回调(如返回缓存值)
用反射提取注解并构造限流上下文
在 AOP 切面中,通过反射获取目标方法上的注解实例,并解析出完整执行上下文:
- 调用
joinPoint.getSignature()获取MethodSignature,再用getMethod().getAnnotation(RateLimit.class)拿到注解对象 - 用
DefaultParameterNameDiscoverer提取方法参数名,结合ExpressionParser(如 Spring 的 SpelExpressionParser)安全求值 key 表达式 - 将解析后的 key(如
"user_123:VIP")、窗口、阈值组装为RateLimiterContext,作为后续限流决策的唯一依据
统一限流引擎:脱离单机内存,对接分布式中间件
单机 Guava RateLimiter 不适用于集群,必须下沉到共享存储:
- 推荐使用 Redis + Lua 脚本实现原子计数+过期设置,例如
INCR key+EXPIRE key window合并在一个 Lua 中,避免竞态 - Key 设计建议:
rl:{service-name}:{context-key}:{window-hash},其中window-hash可基于当前时间戳向下取整(如分钟窗口用ts / 60),确保同一窗口内请求打到同一个 key - 若对延迟极度敏感,可用 Redisson 的
RRateLimiter,它已封装好令牌桶语义和自动续期逻辑
防重入与幂等协同设计
限流本身不解决重复提交,但可与防重入联动提升健壮性:
- 在限流前,先校验请求唯一标识(如 header 中的
X-Request-ID或 body 中的traceId)是否已在 Redis 存在;存在则直接返回 409 Conflict - 或复用同一套 key 生成逻辑:把限流 key 扩展为
req:{key}和rl:{key},用SETNX + EXPIRE实现请求首次准入,再进限流判断 - 注意:防重入有效期应略长于限流窗口(如限流窗口 60s,防重入设 90s),防止窗口切换瞬间出现重复











