SpringCloud Alibaba微服务之自定义超过限流阈值后提示信息和异常处理

215 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

前言

本篇文章介绍在使用流控规则和热点规则后,当请求或者由 @SentinelResource 标注的具体方法,超过限流阈值时,如果自定义其返回的提示信息或者处理其异常并返回我们能接受的信息。希望观众老爷们多多支持,并在评论区批评指正!

问题

我们已经知道了 Sentinel 流控规则和热点规则,我们发现当请求/具体方法超过设定的限流阈值时,会出现以下三种情况:

  1. 对于不添加 @SentinelResource 注解的请求,添加流控规则后,超过阈值,会在页面打印以下信息:

  1. 对于添加了 @SentinelResource 注解的具体方法,设置流控规则后,会抛出异常 FlowException

页面打印如下:

  1. 对于添加了 @SentinelResource 注解的具体方法,设置热点规则后,会抛出异常 ParamFlowException

页面打印如下:

以上三种请求超过限流阈值时,返回的信息不利于我们前后端交互,那么我们怎么修改呢?可以通过以下方式进行修改。

1. 对于未标注 @SentinelResource 注解的请求

对于未标注 @SentinelResource 注解的请求,超过限流阈值后只会返回默认的提示信息:

现在我们需要自定义返回一个提示信息,如返回 json 格式用于和前端联调:

  1. 首先我们需要创建好超过限流阈值后需要返回的内容,我们在 ReturnTipController 中定义一个请求映射,用于返回我们的自定义提示信息:

  1. 接着我们在配置文件中将此页面设定为限流页面

  1. 重启 borrow-service ,进行测试:

2. 对于标注 @SentinelResource 注解的具体方法

对于标注了 @SentinelResource 注解的具体方法,设置流控规则或者热点规则后,超过限流阈值时,分别会抛出 sentinel中的 FlowExceptionParamFlowException 异常,并在页面上打印错误信息。

2.1. 通过 @SentinelResouce注解的 blockHandler 属性来解决

通过 @SentinelResouce注解的 blockHandler 属性我们可以解决当超过限流阈值时,sentinel 抛出的异常。该属性是通过指定一个替代方案来解决(当出现异常时会直接执行我们的替代方法并返回)。

我们还是针对 getUserBorrowDetailByUid 方法进行限流,通过在 @SentinelResoure 注解的 blockHandler 属性,来指定限流之后的替代解决方案,这样就不会抛出默认的异常形式了。

如图,我们为其指定了限流后的替代解决方案 blocked ,表示当限流后返回一个空的信息。

然后我们重启服务,进行测试,可以看到返回的结果是(注意在 sentinel上重新定义方法限流规则):就可以看到返回的空信息了。

当然我们也可以返回统一的接口规范,这里只做简单演示。

注意 blockHandler指定的方法的参数需与被限流方法的参数保持一致,且需在末尾添加 BlockException 类型的参数。否则不会生效,依然返回错误信息。

2.2. 非超过限流阈值抛出异常怎么解决?

我们发现当具体方法中,出现了异常,如空指针异常,除 0 异常等,blockHandler 指定的方法是无法处理的,blockHandler 指定的方法超过限流阈值后抛出的异常。如果是方法本身抛出的其他类型异常,不在管控范围内,但是可以通过 @SentineResourcefallbackexceptionToIgnore 属性来进行处理。


比如我们定义一个接口,其出现了除数为 0异常:

我们可以通过 fellback 属性,指定异常的处理方法,该方法的返回结果会响应至客户端。

通过 exceptionToIgnore属性指定忽略那些异常,也就是这些异常不使用替代方案。

然后重启我们的服务进行测试:localhost:8082/test,然后就看到了页面打印出了异常信息。

注意:

  1. 这种方式会在没有配置blockHandler的情况下,会将Sentinel机制内(也就是限流的异常)的异常也一并处理了。

我们为 /test 添加限流,进行测试:

可以发现打印的是异常信息

  1. 如果配置了blockHandler,那么在超过限流阈值时,只会执行 fallback指定的处理方法,而不会执行 blockHandler 的指定的处理方法。因为 fallback 默认会对所以出现的异常,使用其指定的方法进行处理。

那么我们能不能通过 exceptionToIgnore来排除 sentinel 的异常 FlowExceptionParamFlowException ,从而执行 blockHandler 指定的处理方法呢?

答案是不能。


接下来我们通过为一个具体方法设置热点规则,来说明另一个问题:

如我们为 /test1 接口方法添加 @SentinelResource 注解并指定 fallback

然后我们重启服务,重新设置热点限流规则,进行测试:

发现还是报错,原来这个异常处理方法参数必须包含限流方法的参数,且返回值类型必须为字符串 String 否则不会生效。

这样设置

2.3. 另一种解决方式

我们知道 SpringMVC为我们提供了统一的异常处理方式。可以把 Controller 层的异常进行统一处理。

那么能不能处理超过限流阈值的异常呢,我们来尝试一下。

  1. com.yanghi.study.handler包下创建全局异常处理类:

  1. 去掉 blockHandlerfallback 属性,使其超过限流阈值后抛出异常:

  1. 重启服务,设置限流规则,进行测试:

可以发现能够被我们全局异常处理器捕获,并返回信息,但是无法获取异常信息。

3. 总结

通过以上的探讨,对未添加 @SentinelResource 注解的请求指定限流规则后,超过限流阈值时,返回的提示信息,我们可以通过指定自定义限流页面 block-page ,来自定义提示信息;

针对添加 @SentinelResource 注解的具体方法,可以通过 blockHandler 指定超过限流阈值后的处理方法,替代默认的异常抛出。

针对添加 @SentinelResource 注解的具体方法,其方法体内出现异常,如空指针异常等,可以使用 fallback 指定异常处理方法。

注意

对于 blockHandlerfallback指定的处理方法,必须包含被限流方法的参数,否则处理方法不会生效。另外 fallback 指定的处理方法的返回值类型必须为 String,否则处理方法不会生效。