限流时需主动抛出自定义异常并由全局处理器统一响应,而非依赖框架自动包装;Sentinel中捕获BlockException后转为RateLimitException,再通过@ControllerAdvice返回429 JSON格式错误;降级应在fallback中直接返回兜底结果,避免抛异常;网关层限流更优,可直接返回429而不进入服务。

在限流降级场景中,Java 自定义异常本身不会自动“返回特定错误”,关键在于你如何在限流触发时主动抛出该异常,并在统一异常处理层将其转化为前端友好的响应(如 JSON 错误码 + 提示)。
限流时主动抛出自定义异常
不要依赖框架“自动包装”你的异常。以 Sentinel 为例,在资源被限流时,默认抛出 BlockException。你需要用 try-catch 捕获它,并立即抛出你自己的业务异常(如 RateLimitException):
- 定义自定义异常(继承
RuntimeException即可,无需 checked):
private final int code = 429;
private final String message = "请求过于频繁,请稍后再试";
public RateLimitException() { super("rate limit exceeded"); }
}
- 在 Sentinel 资源入口处捕获并转换:
public Result
return userService.getById(id);
}
public Result
throw new RateLimitException(); // 主动抛出你的异常
}
全局异常处理器统一响应格式
Spring Boot 中用 @ControllerAdvice 拦截你的自定义异常,返回标准 JSON 结构:
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
@ResponseBody
public Result
return Result.fail(429, e.getMessage()); // 如 {"code":429,"msg":"请求过于频繁...","data":null}
}
这样前端收到的就是明确的 429 状态码和可解析的业务错误信息,而非默认的 HTML 错误页或空响应。
立即学习“Java免费学习笔记(深入)”;
注意降级与限流的异常区分
限流(block)和降级(fallback)是两个独立逻辑,异常来源不同:
- 限流触发 →
BlockException(需手动转为自定义异常) - 降级触发(如熔断)→ 默认走
fallback方法,它不抛异常,而是返回兜底值;若你希望降级也返回错误,就在fallback方法里直接返回Result.fail(...),而不是抛异常
避免在 fallback 中抛异常再让全局处理器捕获——这会绕过降级设计本意,且可能引发二次异常。
配合网关层做前置限流(推荐)
如果使用 Spring Cloud Gateway 或 API 网关,建议优先在网关层配置限流规则(如 Redis RateLimiter)。网关限流失败时,会直接返回 429 响应,不进入下游服务,自然也不涉及 Java 异常转换。此时你的服务只需专注业务逻辑,无需处理限流异常。
只有当限流逻辑必须放在业务服务内部(如按用户维度、带业务上下文的复杂规则)时,才需要自定义异常+全局处理这套机制。


















