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

  1. Raise @RateLimiter(count, time) if the limit is genuinely too low for legit traffic.
  2. Back off on the client (exponential backoff / respect Retry-After) when 429 is returned.
  3. Choose a finer keyResolver so the limit is per-user rather than global.
  4. 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

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.