欢迎光临
我们一直在努力

Sentinel BlockException 全链路异常处理机制深度解析

摘要:Sentinel 的 BlockException 是流量管控的统一警报,但它的处理路径却因代码写法不同而错综复杂。本文将带您从顶层设计出发,梳理 Web 拦截器、@SentinelResource 注解、OpenFeign 调用以及硬编码 SphU 四种场景下的异常处理链路,并补充 Spring Boot 默认错误处理 这一终极兜底。读完你将对“限流异常去了哪、业务报错谁接管”了如指掌。

一、一切从 BlockException 说起

无论是流控、熔断降级、热点参数限流还是系统保护,Sentinel 统一向外抛出 BlockException。我们处理这些异常的方案,本质上就是围绕 BlockException 设计的多层拦截与回调。

核心前提:下面介绍的每一个分支,都代表 Sentinel 中定义的一种资源。我们可以针对这些资源灵活配置流控、熔断等规则,一旦请求不满足规则,就会抛出 BlockException,从而进入对应的异常处理链路。

整体处理逻辑:根据代码写法,Sentinel 提供了 4 条专属处理分支;每条分支有对应的拦截器/回调;一旦这些专属处理器失效(内部再次抛出异常),则逐层向外递进,最终由 Spring Boot 默认错误处理机制收底。

二、分支1:纯 Web 接口(未加 @SentinelResource)

普通 Controller 示例:

@RestControllerpublic class HelloController { @GetMapping("/hello") public String hello() { return "Hello Sentinel"; }}

处理链路:

1. 拦截器 SentinelWebInterceptor所有 HTTP 请求都会被它拦截,执行规则校验,触发限流时直接抛出 BlockException。

2. 默认处理器 DefaultBlockExceptionHandler框架内置的默认处理,返回一段英文提示,对用户不友好。

3. 可扩展:自定义 BlockExceptionHandler实现该接口并注册为 Bean,全局接管所有 Web 接口的限流异常:

@Componentpublic class MyBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\\"code\\":429,\\"msg\\":\\"请求太频繁了,请稍后再试\\"}"); }}

关键特性:

  • 作用范围:所有 Controller 接口,无需任何注解。
  • 只能处理 BlockException,业务异常(空指针、SQL 错误)不会走这里。
  • 优先级高于任何 Spring 全局异常处理器。

三、分支2:@SentinelResource 注解(推荐用在 Service 层)

实践建议:@SentinelResource 一般标注在非 Controller 资源(如 Service 方法)上。这样做的原因:

  • Controller 层的限流应由 BlockExceptionHandler 统一处理,保证返回格式一致。
  • Service 层可以针对具体业务做精细的降级逻辑(如查询缓存、返回兜底值),避免与 Web 层处理冲突。

示例:

@Servicepublic class OrderService { @SentinelResource(value = "createOrder", blockHandler = "block", fallback = "fallback") public String createOrder(String userId) { // 业务逻辑,可能抛 RuntimeException return "订单创建成功"; } // 限流/熔断回调,必须带 BlockException 参数 public String block(String userId, BlockException e) { return "当前下单人数过多,请稍后再试"; } // 业务异常兜底回调 public String fallback(String userId, Throwable e) { return "下单失败,请重试"; }}

底层由 SentinelResourceAspect 切面实现,严格遵循两条优先级:

1. blockHandler(优先执行)只要触发 Sentinel 规则抛出 BlockException,并且配置了 blockHandler,就一定会先进入该方法。

2. fallback(次级兜底)

  • 业务代码自身抛出的异常(空指针、SQL 错误等),直接进入 fallback。
  • 若未指定 blockHandler,那么 BlockException 也会降级进入 fallback。

容易混淆的点:如果 blockHandler 或 fallback 内部又抛出异常,该异常不会被 @SentinelResource 再次消化,而是继续向外抛出,交给更外层的异常处理机制(全局异常处理器或默认错误处理)。

四、分支3:OpenFeign 远程调用

使用 spring-cloud-starter-alibaba-sentinel 集成 Feign 时:

@FeignClient(name = "provider", fallback = ProviderFallback.class)public interface ProviderClient { @GetMapping("/provide") String provide();}@Componentclass ProviderFallback implements ProviderClient { @Override public String provide() { return "provider-fallback"; }}

内部机制:

1. 底层通过 SentinelFeign.builder() 构建具有 Sentinel 防护的 Feign 客户端。2. Feign 调用中出现的所有异常(连接超时、对方限流返回的 BlockException、网络错误等)统一进入 fallback 实现类,不区分 blockHandler。3. 同样,若 fallback 类内抛出异常,将继续向外传播。

五、分支4:手动编码 SphU(硬编码模式)

不依赖注解或拦截器,直接在代码中定义资源:

try (Entry entry = SphU.entry("resourceName")) { // 业务代码} catch (BlockException e) { // 手动处理限流降级逻辑}

这是最高优先级的异常处理方式:你完全掌控 BlockException 的捕获,可以自行处理或重新抛出。若 catch 块内部再次抛出异常,该异常将进入后续的全局处理链。

六、兜底链条:从全局异常处理器到 Spring Boot 默认机制

1. Spring @RestControllerAdvice 全局异常处理器

当前面 4 个分支的专属回调或处理器内部再次抛出异常时,才会触发全局异常处理器:

@RestControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler(Throwable.class) public Result handle(Throwable e) { // 记录日志、返回统一错误结果 }}

注意:

  • 正常的 Sentinel BlockException 不会优先进入这里,因为已被前面的专属处理器“劫持”。
  • 如果全局异常处理器也未匹配 BlockException(比如你只处理了 BizException),或者根本没有定义 @RestControllerAdvice,那么异常将继续向上传递。

2. Spring Boot 默认错误处理机制(最终兜底)

当以下情况发生时,异常会到达 Spring Boot 默认错误处理:

  • @SentinelResource 的 blockHandler 和 fallback 均未配置(或都抛出新异常);
  • 没有自定义的 BlockExceptionHandler(或处理失败);
  • 全局 @RestControllerAdvice 不存在,或未涵盖该异常类型。

此时,Spring Boot 的 BasicErrorController 将接管请求,根据请求头返回 HTML 错误页面或 JSON 格式错误信息(/error 端点)。这就是我们经常看到的 “Whitelabel Error Page” 或默认 JSON 响应。

这一层是 Spring Boot 应用的最后一道防线,保证异常至少能得到基本处理,避免直接暴露容器堆栈。

七、全链路优先级终极排序(自顶向下,逐层兜底)

  • 手动 SphU try-catch(最高,自主捕获)
  • @SentinelResource 的 blockHandler(Sentinel 拦截专属回调)
  • @SentinelResource 的 fallback / Feign 的 fallback
  • 自定义全局 BlockExceptionHandler(Web 接口统一处理)
  • Sentinel 默认 DefaultBlockExceptionHandler
  • Spring @RestControllerAdvice 全局异常处理器
  • Spring Boot 默认错误处理(BasicErrorController)(终极兜底)
  • 八、一句话总结

    Web 接口无注解 → 走拦截器 + 自定义 BEH;有 @SentinelResource → blockHandler 管限流,fallback 管异常,一般用在 Service 层;Feign 调用 → 统一 fallback 降级;硬编码 → 自己 try-catch;所有处理器二次报错 → 经过全局异常处理器,最终由 Spring Boot 默认错误处理 兜底。

    理解这套层次,你就能够从容设计统一且健壮的 Sentinel 降级体系,确保无论异常发生在哪个环节,用户都能收到合理响应,运维也能快速定位问题。


    拓展思考:实践中最稳健的做法是——Web 层交给 BlockExceptionHandler,Service 层按需使用 @SentinelResource,同时在 blockHandler 或 fallback 中额外记录监控日志,最后配置一个全局 @RestControllerAdvice 处理所有“漏网”的异常。如此一来,即使某个回调写 bug,也不会把 Tomcat 的 500 堆栈直接甩给客户端。

    赞(0)
    未经允许不得转载:171主机测评 » Sentinel BlockException 全链路异常处理机制深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址