YunaiV/ruoyi-vue-pro · warning · ServiceException
429
429
Error message
请求过于频繁,请稍后重试
What it means
RateLimiterAspect applies a Redis token-bucket/counter limit per @RateLimiter. When tryAcquire returns false (count exceeded within the time window for the resolved key), it throws ServiceException(TOO_MANY_REQUESTS code 429, default '请求过于频繁,请稍后重试'). This is the intended throttle response, surfaced as a ServiceException the global handler maps to HTTP 429.
Source
Thrown at yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/aop/RateLimiterAspect.java:55
this.rateLimiterRedisDAO = rateLimiterRedisDAO;
}
@Before("@annotation(rateLimiter)")
public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {
// 获得 RateLimiterKeyResolver 对象
RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver());
Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver");
// 解析 Key
String key = keyResolver.resolver(joinPoint, rateLimiter);
// 获取 1 次限流
boolean success = rateLimiterRedisDAO.tryAcquire(key,
rateLimiter.count(), rateLimiter.time(), rateLimiter.timeUnit());
if (!success) {
log.info("[beforePointCut][方法({}) 参数({}) 请求过于频繁]", joinPoint.getSignature().toString(), joinPoint.getArgs());
String message = StrUtil.blankToDefault(rateLimiter.message(),
GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getMsg());
throw new ServiceException(GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getCode(), message);
}
}
}
View on GitHub (pinned to 0418084e22)
Solutions
- Raise @RateLimiter(count, time) if the limit is genuinely too low for legit traffic.
- Back off on the client (exponential backoff / respect Retry-After) when 429 is returned.
- Choose a finer keyResolver so the limit is per-user rather than global.
- Return a proper 429 with headers so clients throttle themselves.
Example fix
// before @RateLimiter(keyResolver = DefaultRateLimiterKeyResolver.class, count = 1, time = 1) // after @RateLimiter(keyResolver = UserRateLimiterKeyResolver.class, count = 10, time = 1)
Defensive patterns
Strategy: try-catch
Validate before calling
boolean ok = rateLimiterRedisDAO.tryAcquire(key, count, time, unit); if (!ok) throw new ServiceException(TOO_MANY_REQUESTS);
Type guard
null
Try / catch
try { return aspect.beforePointCut(pjp, rateLimiter); }
catch (ServiceException e) { if (e.getCode()==429) { setRetryAfter(response, 1); return; } throw e; } Prevention
- Back off on the client when 429 is returned
- Choose a per-user keyResolver
- Tune @RateLimiter count/time to real usage
When it happens
Trigger: A client exceeds the configured @RateLimiter count within time/timeUnit for the resolved key (per user, per IP, or per method); an automated/scripted client hammering the endpoint; key resolves to the same value for many users (collisions).
Common situations: Front-end polling/refresh loops; bot traffic; misconfigured rate that is too low for normal usage; keyResolver too coarse.
Related errors
AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14).
Data as JSON: /api/errors/8c1650c3d78b9143.
Report an issue: GitHub.