Sentinel 的 @SentinelResource 注解中,blockHandler 和 fallback 有什么区别?
在 Sentinel 的 @SentinelResource 注解中,blockHandler 和 fallback 都用于处理方法执行失败时的“兜底”逻辑,但它们的触发条件、处理范围和设计初衷有非常明确的区别。
简单来说:blockHandler 管的是“被 Sentinel 限制了怎么办”,fallback 管的是“代码报错了怎么办”。
以下是详细的区别和对比:
1. blockHandler(限流/熔断兜底)
- 触发条件:只有当触发了 Sentinel 的规则(如限流、熔断降级、系统保护、热点参数等),抛出
BlockException异常时,才会进入blockHandler指定的方法。 - 处理范围:仅处理 Sentinel 内部的控制异常(
BlockException及其子类,如FlowException,DegradeException等)。如果你的业务代码里抛出了NullPointerException或数据库异常,blockHandler不会生效。 - 方法签名要求:
- 返回值类型必须与原方法一致。
- 参数列表必须与原方法一致,并且在最后追加一个
BlockException类型的参数。 - (可选)如果是将其定义在其他类中,必须是
static方法,并通过blockHandlerClass属性指定。
2. fallback(业务异常兜底)
- 触发条件:当原方法抛出任何异常(默认情况下包含所有的
Throwable)时,都会进入fallback指定的方法。它类似于try-catch里的catch块。 - 处理范围:处理业务代码运行过程中的异常(如
RuntimeException,TimeoutException等)。可以通过exceptionsToIgnore属性排除掉某些不需要兜底的异常。 - 方法签名要求:
- 返回值类型必须与原方法一致。
- 参数列表必须与原方法一致,可以额外在最后追加一个
Throwable类型的参数(用于接收异常,非必须)。 - (可选)如果是将其定义在其他类中,必须是
static方法,并通过fallbackClass属性指定。
3. 核心区别对比表
| 特性 | blockHandler (限流处理) |
fallback (降级/异常处理) |
|---|---|---|
| 设计目的 | 处理 Sentinel 规则触发后的逻辑(如被限流提示“系统繁忙”) | 处理业务代码抛出异常后的逻辑(如查询数据库失败则查询缓存) |
| 触发异常 | 仅限 BlockException |
所有异常 (Throwable),包含业务报错 |
| 附加参数要求 | 必须在参数最后加上 BlockException e |
可在最后加上 Throwable e(可选) |
| 异常排除 | 不支持排除异常 | 支持通过 exceptionsToIgnore 排除特定异常 |
4. 关键问题:如果两者同时配置了怎么办?
如果一个方法同时配置了 blockHandler 和 fallback:
- 当业务代码抛出普通异常时(如空指针):只会触发
fallback。 - 当触发 Sentinel 限流/熔断规则抛出
BlockException时:blockHandler的优先级更高,会进入blockHandler处理,而不会进入fallback。 - 特殊情况:如果只配置了
fallback没有配置blockHandler,那么触发限流抛出的BlockException也会被fallback捕获并处理(因为BlockException也是Throwable的子类)。但强烈建议将两者分开处理,因为限流(流量太大)和业务异常(代码出错)在业务上的应对策略通常是完全不同的。
5. 代码示例说明
java
@Service
public class OrderService {
@SentinelResource(
value = "createOrder",
blockHandler = "handleLimit", // 指定限流处理方法
fallback = "handleError" // 指定异常处理方法
)
public String createOrder(String userId, String productId) {
if ("error".equals(userId)) {
throw new RuntimeException("数据库异常"); // 触发 fallback
}
return "订单创建成功"; // 正常返回,如果请求太快触发限流,则触发 blockHandler
}
// --- blockHandler 处理方法 ---
// 注意:参数必须与原方法完全一致,最后加一个 BlockException
public String handleLimit(String userId, String productId, BlockException ex) {
return "系统当前访问人数过多,请稍后再试!(限流了)";
}
// --- fallback 处理方法 ---
// 注意:参数必须与原方法完全一致,最后加一个 Throwable (可选)
public String handleError(String userId, String productId, Throwable ex) {
return "创建订单失败,系统发生未知错误!(业务报错了)";
}
}
总结最佳实践:
在实际开发中,通常用 blockHandler 返回类似“服务器繁忙请稍后重试”的提示;用 fallback 返回默认数据、查询备用数据库或返回业务处理失败的错误码。两者各司其职。