本次旅程您将获得如下知识Sentinel 的热点参数限流将 Sentinel 热点参数限流规则持久化到 NacosSpring Boot 项目接口统一返回与统一异常处理使用 SentinelResource 注解的 blockHandler 和 fallback 进行优雅返回与解耦本系列文章代码仓库https://github.com/iweidujiang/spring-cloud-alibaba-lab前言本篇文章填上次 将 Sentinel 熔断限流规则持久化到 Nacos 配置中心 提到的一个问题之前我们访问接口进行限流或者降级后直接返回500错误或者提示flow limit之类的东西这样显得很不优雅透漏着不专业那么进行限流或者降级后该如何进行优雅的返回呢本章从 Sentinel 的热点参数限流着手使用注解SentinelResource的blockHandler和fallback来实现限流后优雅的返回。Sentinel的热点参数限流看到限流名字热点参数顾名思义就能想到是针对请求资源的参数进行限流的。如果参数是热点参数并且符合设置的热点参数限流规则那么 Sentinel 也对其进行限流。何为热点参数呢联想一下娱乐圈的那些明星粉丝越多的明星他的热度就越高。同理一个系统访问的数据越频繁这个数据的热度就越高。这种经常访问的数据就是热点。有些时候需要采取一些措施对热点数据进行限制比如统计一段时间内最常访问的某个商品 ID 参数为商品 ID 那么对该商品 ID 进行限制请求资源参数用户 ID 针对一段时间内频繁访问的用户 ID 进行限制等等热点参数限流会统计传入参数中的热点参数并根据配置的限流阈值与模式对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制仅对包含热点参数的资源调用生效。下面我们就开始创建包含热点参数的接口资源并配置热点参数限流规则进行验证 Sentinel 的热点参数限流功能。还是用本系列06-sentinel-block-fallback/sentinel-hotkey-service模块新增一个接口资源如下packageio.github.iweidujiang.lab06.service.controller;importcom.alibaba.csp.sentinel.annotation.SentinelResource;importio.github.iweidujiang.lab06.common.exception.HotKeyBlockedException;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;/** * 热点参数限流测试接口。 * * author 苏渡苇 */RestControllerpublicclassUserController{privatestaticfinalLoggerlogLoggerFactory.getLogger(UserController.class);/** * 查询商品第一个参数 userId 可作为热点参数限流。 */GetMapping(/getProduct)SentinelResource(getProduct)publicStringgetProduct(RequestParam(valueuserId,requiredfalse)LonguserId,RequestParam(valueproductId,requiredfalse)LongproductId,RequestParam(valuecategoryId,requiredfalse)IntegercategoryId){log.info(getProduct param userId{},productId{},categoryId{},userId,productId,categoryId);returngetProduct success;}}下面开始通过 Sentinel 控制台和 Nacos 配置中心两种方式进行规则配置。通过 Sentinel 控制台进行热点参数限流规则配置在控制台在簇点链路上找到对应的资源进行热点规则配置打开热点操作如图所示的就是对资源getProduct的第一个参数userId进行限流如果配置单机阈值为1窗口统计时长1秒那么其含义就是在1秒内访问 getProduct 资源超过1次如果传入了热点参数 userId那么将对该资源进行限流如果没有传入 userId那么该资源不被限流。现在我们来访问接口验证一下先传入不是热点的参数categoryId可以看到无论QPS是多少接口都正常返回没有进行限流现在我们加上热点参数userId试一下1秒内快速刷新几下QPS只要大于1了接口访问就芭比Q了说明 Sentinel 的热点参数限流生效了。Sentinel 热点参数限流规则持久化到 Nacos在 Nacos 设置 Data ID 及规则内容在 Sentinel 控制台配置的规则当服务重启后规则将消失这并不是我们想要的结果上一篇文章中我们已经完成了 Sentinel 的规则持久化到 Nacos 中本小节我们配置一下热点参数的限流规则。热点参数的限流规则主要有以下几个字段resource资源名必填项count限流阈值必填项grade限流模式只能是QPSparamIdx热点参数的索引必填项索引从 0 开始durationInSec统计窗口时间长度单位为秒默认为 1 scontrolBehavior流控效果支持快速失败和匀速排队模式默认为快速失败maxQueueingTimeMs最大排队等待时长仅在匀速排队模式生效默认 0 msparamFlowItemList参数例外项可以针对指定的参数值单独设置限流阈值不受前面count阈值的限制。仅支持基本类型和字符串类型。clusterMode是否是集群参数流控规则默认为falseclusterConfig集群流控相关配置。下面我们在 Nacos 控制台中对资源getProduct添加热点参数限流规则这里的设置和前文在 Sentinel 控制台设置的含义一致。配置 application.ymlNacos 中持久化了限流规则那么在微服务项目中可以在application.yml配置文件中这样配置spring:application:name:sentinel-hotkey-servicecloud:nacos:discovery:server-addr:${NACOS_SERVER:127.0.0.1:8848}sentinel:transport:dashboard:${SENTINEL_DASHBOARD:localhost:8080}port:8722web-context-unify:falsedatasource:flow:nacos:server-addr:${NACOS_SERVER:127.0.0.1:8848}groupId:DEFAULT_GROUPdataId:sentinelFlowRule.jsonrule-type:flowparam-flow:nacos:server-addr:${NACOS_SERVER:127.0.0.1:8848}groupId:DEFAULT_GROUPdataId:hotKeyRule.jsonrule-type:param-flow热点参数规则 JSON 示例见仓库06-sentinel-block-fallback/config/hotKeyRule.json。其中ruleType为param-flow的就是热点参数限流的规则一定要把dataId配置正确。然后启动服务重新验证限流规则依然生效接口统一返回和统一异常处理前面接口getProduct调用成功返回字符串getProduct success,调用失败返回了一个500 Internal Server Error这种返回格式没有一个统一的格式这在前后端分离项目中并不是很好的实践。前后端应该约定好每个接口的统一返回格式。本文在06-sentinel-block-fallback模块下增加子模块lab-common作为公共模块其他子模块可以引入该模块使用。lab-common子模块中定义了接口返回格式在包io.github.iweidujiang.lab06.common.response下定义两个类枚举 ResponseCodepublicenumResponseCode{/** * 操作成功 */SUCCESS(00000,操作成功),/** * 系统内部异常 */INTERNAL_ERROR(B0001,系统执行出错);/** * 状态码 */privatefinalStringcode;/** * 提示信息 */privatefinalStringmessage;ResponseCode(Stringcode,Stringmessage){this.codecode;this.messagemessage;}publicStringgetCode(){returncode;}publicStringgetMessage(){returnmessage;}}该枚举类可以定义很多其他业务上的异常代码和异常信息。统一返回类 ResponseResult/** * 统一 API 返回结果。 * * param T 数据类型 * author 苏渡苇 */publicclassResponseResultT{// 详见仓库 lab-common 模块}06-sentinel-block-fallback/pom.xml定义子模块modulesmodulelab-common/modulemodulesentinel-hotkey-service/module/modules在sentinel-hotkey-service中引入lab-commondependencygroupIdio.github.iweidujiang/groupIdartifactIdlab-common/artifactIdversion1.0.0/version/dependencyOK至此基础信息准备完毕下面开始设置统一返回结果。使用 Spring 的ResponseBodyAdvice类以及RestControllerAdvice注解来完成接口的统一返回在sentinel-hotkey-service中新建全局返回配置类ResponseAdvice完整代码见仓库io.github.iweidujiang.lab06.service.response.ResponseAdvice。这样接口就有了统一返回的格式了。另外我们可以注意到资源被限流后返回的信息也被如下方法截获ExceptionHandler(Exception.class)publicResponseResultStringexception(Exceptione){LOGGER.error(未知异常,e);returnResponseResult.fail(ResponseCode.INTERNAL_ERROR.getCode(),ResponseCode.INTERNAL_ERROR.getMessage());}这里定义的是所有的异常都返回{code:B0001,message:系统执行出错,data:null}而资源被Sentinel限流就是返回一个BlockException异常所以也就被我们定义的通用异常方法统一处理了。SentinelResource 注解之 blockHandler 和 fallbackblockHandler 和 blockHandlerClass使用 blockHandler 处理限流逻辑前面的返回{code:B0001,message:系统执行出错,data:null}虽然格式上没有问题但是返回的结果语义太过笼统这里我们可以使用注解SentinelResource的blockHandler属性来自定义限流后的处理逻辑。请求资源被Sentinel限流后将会出现BlockException异常blockHandler对应处理BlockException的函数名称。blockHandler 函数访问范围需要是public返回类型需要与原方法相匹配参数类型需要和原方法相匹配并且最后加一个额外的参数类型为BlockException。blockHandler 函数默认需要和原方法在同一个类中。下面我们来增加一个blockHandler对应的处理方法GetMapping(/getProduct)SentinelResource(valuegetProduct,blockHandlergetProductBlockHandler)publicStringgetProduct(RequestParam(valueuserId,requiredfalse)LonguserId,RequestParam(valueproductId,requiredfalse)LongproductId,RequestParam(valuecategoryId,requiredfalse)IntegercategoryId){log.info(getProduct param userId{},productId{},categoryId{},userId,productId,categoryId);returngetProduct success;}publicStringgetProductBlockHandler(LonguserId,LongproductId,IntegercategoryId,BlockExceptionblockException){return热点参数 userIduserId 限流;}然后在测试调用资源结果如下可以看到将getProductBlockHandler处理结果返回了。这样看还是感觉怪怪的因为资源被限流了其实返回的是异常结果这里希望最好给我返回个指定的code以及message。再升级一下处理逻辑首先定义限流返回码在lab-common模块的 ResponseCode 枚举类增加/** * 热点参数限流 */HOT_KEY_BLOCKED(B0002,热点参数限流);然后自定义一个异常HotKeyBlockedException位于io.github.iweidujiang.lab06.common.exception/** * 热点参数限流异常。 * * author 苏渡苇 */publicclassHotKeyBlockedExceptionextendsRuntimeException{privatefinalObjecthotKey;publicHotKeyBlockedException(ObjecthotKey){super(热点参数 [hotKey] 限流);this.hotKeyhotKey;}publicObjectgetHotKey(){returnhotKey;}}在全局异常处理类GlobalExceptionHandler中增加/** * 热点参数限流异常 * param e 异常对象 * return ResponseResult 全局异常响应 */ExceptionHandler(HotKeyBlockedException.class)publicResponseResultStringhotKeyBlockedException(Exceptione){LOGGER.error(热点参数限流,e);returnResponseResult.fail(ResponseCode.HOT_KEY_BLOCKED.getCode(),ResponseCode.HOT_KEY_BLOCKED.getMessage());}最后修改一下getProductBlockHandler方法publicStringgetProductBlockHandler(LonguserId,LongproductId,IntegercategoryId,BlockExceptionblockException){thrownewHotKeyBlockedException(userId);}这样就可以返回想要的结果使用 blockHandlerClass 将业务和限流处理方法解耦通过前面的处理其实我们可以发现限流处理的方法和业务逻辑耦合在一起了即处理方法就在业务逻辑本类中。如果每个业务都增加一个自己的限流处理逻辑那么类将会膨胀的难以维护因此SentinelResource除了blockHandler可以设置自定义限流处理逻辑方法以外还提供另外一个属性来设置限流处理逻辑类型blockHandlerClass属性。也就是说如果我们希望使用其他类的函数来自定义限流处理则可以指定blockHandlerClass为对应的类的Class对象。 Sentinel 规定对应的函数必需为 static 函数否则无法解析。继续上代码只需将getProductBlockHandler移动到HotKeyBlockedException类中并将其设为 static 的即可publicclassHotKeyBlockedExceptionextendsRuntimeException{privateObjecthotKey;publicHotKeyBlockedException(ObjecthotKey){super(热点参数 [hotKey]限流);this.hotKeyhotKey;}publicstaticStringgetProductBlockHandler(LonguserId,LongproductId,IntegercategoryId,BlockExceptionblockException){thrownewHotKeyBlockedException(userId);}publicObjectgetHotKey(){returnhotKey;}}然后在资源上这样定义SentinelResource(valuegetProduct,blockHandlerClassHotKeyBlockedException.class,blockHandlergetProductBlockHandler)这样就完成了限流处理逻辑与业务逻辑的解耦。fallbackfallback同blockHandler的使用方法及其相似只是他们的本质不同blockHandler针对 Sentinel 配置的规则生效而产生 BlockException 异常时的对应处理fallback可以针对所有类型的异常除了exceptionsToIgnore里面排除掉的异常类型进行处理。若blockHandler和fallback都进行了配置则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。若未配置blockHandler、fallback则被限流降级时会将BlockException直接抛出。由于fallback和blockHandler的用法实在太像了在UserController中增加fallback属性即可SentinelResource(valuegetProduct,blockHandlerClassHotKeyBlockedException.class,blockHandlergetProductBlockHandler,fallbackgetProductFallback)publicStringgetProduct(...){if(userId!nulluserId0){thrownewIllegalArgumentException(userId 不能为负数);}returngetProduct success;}/** * fallback 处理非 BlockException 的业务异常。 */publicStringgetProductFallback(LonguserId,LongproductId,IntegercategoryId,Throwablethrowable){returngetProduct fallback: throwable.getMessage();}完整代码见仓库https://github.com/iweidujiang/spring-cloud-alibaba-lab/tree/main/06-sentinel-block-fallback小结可以通过SentinelResource注解来定义资源并使用其属性blockHandler和fallback自定义异常处理逻辑。其中blockHandler是针对限流熔断异常而设置的属性fallback是针对所有异常的。如果同时配置了blockHandler和fallback属性则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。本文还提供了 Spring Boot 统一返回结果和全局异常处理的一种方法。以上本次导航结束。点个赞再走吧~本次导航结束。先赞后看养成习惯。举手之劳赞有余香。本文创作于 2022-08-17 。代码仓库已更新https://github.com/iweidujiang/spring-cloud-alibaba-lab
Sentinel限流降级如何优雅的返回?SentinelResource注解之blockHander和fallback
本次旅程您将获得如下知识Sentinel 的热点参数限流将 Sentinel 热点参数限流规则持久化到 NacosSpring Boot 项目接口统一返回与统一异常处理使用 SentinelResource 注解的 blockHandler 和 fallback 进行优雅返回与解耦本系列文章代码仓库https://github.com/iweidujiang/spring-cloud-alibaba-lab前言本篇文章填上次 将 Sentinel 熔断限流规则持久化到 Nacos 配置中心 提到的一个问题之前我们访问接口进行限流或者降级后直接返回500错误或者提示flow limit之类的东西这样显得很不优雅透漏着不专业那么进行限流或者降级后该如何进行优雅的返回呢本章从 Sentinel 的热点参数限流着手使用注解SentinelResource的blockHandler和fallback来实现限流后优雅的返回。Sentinel的热点参数限流看到限流名字热点参数顾名思义就能想到是针对请求资源的参数进行限流的。如果参数是热点参数并且符合设置的热点参数限流规则那么 Sentinel 也对其进行限流。何为热点参数呢联想一下娱乐圈的那些明星粉丝越多的明星他的热度就越高。同理一个系统访问的数据越频繁这个数据的热度就越高。这种经常访问的数据就是热点。有些时候需要采取一些措施对热点数据进行限制比如统计一段时间内最常访问的某个商品 ID 参数为商品 ID 那么对该商品 ID 进行限制请求资源参数用户 ID 针对一段时间内频繁访问的用户 ID 进行限制等等热点参数限流会统计传入参数中的热点参数并根据配置的限流阈值与模式对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制仅对包含热点参数的资源调用生效。下面我们就开始创建包含热点参数的接口资源并配置热点参数限流规则进行验证 Sentinel 的热点参数限流功能。还是用本系列06-sentinel-block-fallback/sentinel-hotkey-service模块新增一个接口资源如下packageio.github.iweidujiang.lab06.service.controller;importcom.alibaba.csp.sentinel.annotation.SentinelResource;importio.github.iweidujiang.lab06.common.exception.HotKeyBlockedException;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;/** * 热点参数限流测试接口。 * * author 苏渡苇 */RestControllerpublicclassUserController{privatestaticfinalLoggerlogLoggerFactory.getLogger(UserController.class);/** * 查询商品第一个参数 userId 可作为热点参数限流。 */GetMapping(/getProduct)SentinelResource(getProduct)publicStringgetProduct(RequestParam(valueuserId,requiredfalse)LonguserId,RequestParam(valueproductId,requiredfalse)LongproductId,RequestParam(valuecategoryId,requiredfalse)IntegercategoryId){log.info(getProduct param userId{},productId{},categoryId{},userId,productId,categoryId);returngetProduct success;}}下面开始通过 Sentinel 控制台和 Nacos 配置中心两种方式进行规则配置。通过 Sentinel 控制台进行热点参数限流规则配置在控制台在簇点链路上找到对应的资源进行热点规则配置打开热点操作如图所示的就是对资源getProduct的第一个参数userId进行限流如果配置单机阈值为1窗口统计时长1秒那么其含义就是在1秒内访问 getProduct 资源超过1次如果传入了热点参数 userId那么将对该资源进行限流如果没有传入 userId那么该资源不被限流。现在我们来访问接口验证一下先传入不是热点的参数categoryId可以看到无论QPS是多少接口都正常返回没有进行限流现在我们加上热点参数userId试一下1秒内快速刷新几下QPS只要大于1了接口访问就芭比Q了说明 Sentinel 的热点参数限流生效了。Sentinel 热点参数限流规则持久化到 Nacos在 Nacos 设置 Data ID 及规则内容在 Sentinel 控制台配置的规则当服务重启后规则将消失这并不是我们想要的结果上一篇文章中我们已经完成了 Sentinel 的规则持久化到 Nacos 中本小节我们配置一下热点参数的限流规则。热点参数的限流规则主要有以下几个字段resource资源名必填项count限流阈值必填项grade限流模式只能是QPSparamIdx热点参数的索引必填项索引从 0 开始durationInSec统计窗口时间长度单位为秒默认为 1 scontrolBehavior流控效果支持快速失败和匀速排队模式默认为快速失败maxQueueingTimeMs最大排队等待时长仅在匀速排队模式生效默认 0 msparamFlowItemList参数例外项可以针对指定的参数值单独设置限流阈值不受前面count阈值的限制。仅支持基本类型和字符串类型。clusterMode是否是集群参数流控规则默认为falseclusterConfig集群流控相关配置。下面我们在 Nacos 控制台中对资源getProduct添加热点参数限流规则这里的设置和前文在 Sentinel 控制台设置的含义一致。配置 application.ymlNacos 中持久化了限流规则那么在微服务项目中可以在application.yml配置文件中这样配置spring:application:name:sentinel-hotkey-servicecloud:nacos:discovery:server-addr:${NACOS_SERVER:127.0.0.1:8848}sentinel:transport:dashboard:${SENTINEL_DASHBOARD:localhost:8080}port:8722web-context-unify:falsedatasource:flow:nacos:server-addr:${NACOS_SERVER:127.0.0.1:8848}groupId:DEFAULT_GROUPdataId:sentinelFlowRule.jsonrule-type:flowparam-flow:nacos:server-addr:${NACOS_SERVER:127.0.0.1:8848}groupId:DEFAULT_GROUPdataId:hotKeyRule.jsonrule-type:param-flow热点参数规则 JSON 示例见仓库06-sentinel-block-fallback/config/hotKeyRule.json。其中ruleType为param-flow的就是热点参数限流的规则一定要把dataId配置正确。然后启动服务重新验证限流规则依然生效接口统一返回和统一异常处理前面接口getProduct调用成功返回字符串getProduct success,调用失败返回了一个500 Internal Server Error这种返回格式没有一个统一的格式这在前后端分离项目中并不是很好的实践。前后端应该约定好每个接口的统一返回格式。本文在06-sentinel-block-fallback模块下增加子模块lab-common作为公共模块其他子模块可以引入该模块使用。lab-common子模块中定义了接口返回格式在包io.github.iweidujiang.lab06.common.response下定义两个类枚举 ResponseCodepublicenumResponseCode{/** * 操作成功 */SUCCESS(00000,操作成功),/** * 系统内部异常 */INTERNAL_ERROR(B0001,系统执行出错);/** * 状态码 */privatefinalStringcode;/** * 提示信息 */privatefinalStringmessage;ResponseCode(Stringcode,Stringmessage){this.codecode;this.messagemessage;}publicStringgetCode(){returncode;}publicStringgetMessage(){returnmessage;}}该枚举类可以定义很多其他业务上的异常代码和异常信息。统一返回类 ResponseResult/** * 统一 API 返回结果。 * * param T 数据类型 * author 苏渡苇 */publicclassResponseResultT{// 详见仓库 lab-common 模块}06-sentinel-block-fallback/pom.xml定义子模块modulesmodulelab-common/modulemodulesentinel-hotkey-service/module/modules在sentinel-hotkey-service中引入lab-commondependencygroupIdio.github.iweidujiang/groupIdartifactIdlab-common/artifactIdversion1.0.0/version/dependencyOK至此基础信息准备完毕下面开始设置统一返回结果。使用 Spring 的ResponseBodyAdvice类以及RestControllerAdvice注解来完成接口的统一返回在sentinel-hotkey-service中新建全局返回配置类ResponseAdvice完整代码见仓库io.github.iweidujiang.lab06.service.response.ResponseAdvice。这样接口就有了统一返回的格式了。另外我们可以注意到资源被限流后返回的信息也被如下方法截获ExceptionHandler(Exception.class)publicResponseResultStringexception(Exceptione){LOGGER.error(未知异常,e);returnResponseResult.fail(ResponseCode.INTERNAL_ERROR.getCode(),ResponseCode.INTERNAL_ERROR.getMessage());}这里定义的是所有的异常都返回{code:B0001,message:系统执行出错,data:null}而资源被Sentinel限流就是返回一个BlockException异常所以也就被我们定义的通用异常方法统一处理了。SentinelResource 注解之 blockHandler 和 fallbackblockHandler 和 blockHandlerClass使用 blockHandler 处理限流逻辑前面的返回{code:B0001,message:系统执行出错,data:null}虽然格式上没有问题但是返回的结果语义太过笼统这里我们可以使用注解SentinelResource的blockHandler属性来自定义限流后的处理逻辑。请求资源被Sentinel限流后将会出现BlockException异常blockHandler对应处理BlockException的函数名称。blockHandler 函数访问范围需要是public返回类型需要与原方法相匹配参数类型需要和原方法相匹配并且最后加一个额外的参数类型为BlockException。blockHandler 函数默认需要和原方法在同一个类中。下面我们来增加一个blockHandler对应的处理方法GetMapping(/getProduct)SentinelResource(valuegetProduct,blockHandlergetProductBlockHandler)publicStringgetProduct(RequestParam(valueuserId,requiredfalse)LonguserId,RequestParam(valueproductId,requiredfalse)LongproductId,RequestParam(valuecategoryId,requiredfalse)IntegercategoryId){log.info(getProduct param userId{},productId{},categoryId{},userId,productId,categoryId);returngetProduct success;}publicStringgetProductBlockHandler(LonguserId,LongproductId,IntegercategoryId,BlockExceptionblockException){return热点参数 userIduserId 限流;}然后在测试调用资源结果如下可以看到将getProductBlockHandler处理结果返回了。这样看还是感觉怪怪的因为资源被限流了其实返回的是异常结果这里希望最好给我返回个指定的code以及message。再升级一下处理逻辑首先定义限流返回码在lab-common模块的 ResponseCode 枚举类增加/** * 热点参数限流 */HOT_KEY_BLOCKED(B0002,热点参数限流);然后自定义一个异常HotKeyBlockedException位于io.github.iweidujiang.lab06.common.exception/** * 热点参数限流异常。 * * author 苏渡苇 */publicclassHotKeyBlockedExceptionextendsRuntimeException{privatefinalObjecthotKey;publicHotKeyBlockedException(ObjecthotKey){super(热点参数 [hotKey] 限流);this.hotKeyhotKey;}publicObjectgetHotKey(){returnhotKey;}}在全局异常处理类GlobalExceptionHandler中增加/** * 热点参数限流异常 * param e 异常对象 * return ResponseResult 全局异常响应 */ExceptionHandler(HotKeyBlockedException.class)publicResponseResultStringhotKeyBlockedException(Exceptione){LOGGER.error(热点参数限流,e);returnResponseResult.fail(ResponseCode.HOT_KEY_BLOCKED.getCode(),ResponseCode.HOT_KEY_BLOCKED.getMessage());}最后修改一下getProductBlockHandler方法publicStringgetProductBlockHandler(LonguserId,LongproductId,IntegercategoryId,BlockExceptionblockException){thrownewHotKeyBlockedException(userId);}这样就可以返回想要的结果使用 blockHandlerClass 将业务和限流处理方法解耦通过前面的处理其实我们可以发现限流处理的方法和业务逻辑耦合在一起了即处理方法就在业务逻辑本类中。如果每个业务都增加一个自己的限流处理逻辑那么类将会膨胀的难以维护因此SentinelResource除了blockHandler可以设置自定义限流处理逻辑方法以外还提供另外一个属性来设置限流处理逻辑类型blockHandlerClass属性。也就是说如果我们希望使用其他类的函数来自定义限流处理则可以指定blockHandlerClass为对应的类的Class对象。 Sentinel 规定对应的函数必需为 static 函数否则无法解析。继续上代码只需将getProductBlockHandler移动到HotKeyBlockedException类中并将其设为 static 的即可publicclassHotKeyBlockedExceptionextendsRuntimeException{privateObjecthotKey;publicHotKeyBlockedException(ObjecthotKey){super(热点参数 [hotKey]限流);this.hotKeyhotKey;}publicstaticStringgetProductBlockHandler(LonguserId,LongproductId,IntegercategoryId,BlockExceptionblockException){thrownewHotKeyBlockedException(userId);}publicObjectgetHotKey(){returnhotKey;}}然后在资源上这样定义SentinelResource(valuegetProduct,blockHandlerClassHotKeyBlockedException.class,blockHandlergetProductBlockHandler)这样就完成了限流处理逻辑与业务逻辑的解耦。fallbackfallback同blockHandler的使用方法及其相似只是他们的本质不同blockHandler针对 Sentinel 配置的规则生效而产生 BlockException 异常时的对应处理fallback可以针对所有类型的异常除了exceptionsToIgnore里面排除掉的异常类型进行处理。若blockHandler和fallback都进行了配置则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。若未配置blockHandler、fallback则被限流降级时会将BlockException直接抛出。由于fallback和blockHandler的用法实在太像了在UserController中增加fallback属性即可SentinelResource(valuegetProduct,blockHandlerClassHotKeyBlockedException.class,blockHandlergetProductBlockHandler,fallbackgetProductFallback)publicStringgetProduct(...){if(userId!nulluserId0){thrownewIllegalArgumentException(userId 不能为负数);}returngetProduct success;}/** * fallback 处理非 BlockException 的业务异常。 */publicStringgetProductFallback(LonguserId,LongproductId,IntegercategoryId,Throwablethrowable){returngetProduct fallback: throwable.getMessage();}完整代码见仓库https://github.com/iweidujiang/spring-cloud-alibaba-lab/tree/main/06-sentinel-block-fallback小结可以通过SentinelResource注解来定义资源并使用其属性blockHandler和fallback自定义异常处理逻辑。其中blockHandler是针对限流熔断异常而设置的属性fallback是针对所有异常的。如果同时配置了blockHandler和fallback属性则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。本文还提供了 Spring Boot 统一返回结果和全局异常处理的一种方法。以上本次导航结束。点个赞再走吧~本次导航结束。先赞后看养成习惯。举手之劳赞有余香。本文创作于 2022-08-17 。代码仓库已更新https://github.com/iweidujiang/spring-cloud-alibaba-lab