往期回顾

Nacos的安装与配置

Spring Cloud集成Nacos作为注册中心

LoadBalacer集成Nacos实现负载均衡

常见的负载均衡策略分析

Spring Cloud集成Dubbo实现RPC调用

SpringCloud集成Nacos作为配置中心

Nacos整合OpenFegin实现RPC调用

Nacos整合Gateway入门实例

Spring Cloud Gateway的过滤器配置

Nacos整合Gateway实现动态路由

Sentinel的安装与配置

前面我们已经简单的介绍了如何安装与使用Sentinel,接下来我们继续来看看Sentinel的一些核心概念以及特性吧

基本概念回顾

我们前面已经介绍过Sentinel的两个核心概念,这里我们再回顾一下吧:

  • Sentinel 的基本概念有两个,它们分别是:资源和规则
基本概念 描述
资源 资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如由应用程序提供的服务或者是服务里的方法,甚至可以是一段代码。 我们可以通过 Sentinel 提供的 API 来定义一个资源,使其能够被 Sentinel 保护起来。通常情况下,我们可以使用方法名、URL 甚至是服务名来作为资源名来描述某个资源。
规则 围绕资源而设定的规则。Sentinel 支持流量控制、熔断降级、系统保护、来源访问控制和热点参数等多种规则,所有这些规则都可以动态实时调整。

定义资源

Sentinel需要先把可能需要保护的资源定义好,之后再配置规则。也可以理解为,只要有了资源,我们就可以在任何时候灵活地定义各种流量控制规则。在编码的时候,只需要考虑这个代码是否需要保护,如果需要保护,就将之定义为一个资源。

Sentinel提供多种定义资源的方式,分别是:

  • 主流框架的默认适配
  • 抛出异常的方式定义资源
  • 返回布尔值方式定义资源
  • 注解方式定义资源
  • 异步调用支持

这里我不再一一的详细介绍,想要详细了解每种方式的同学可以自行查阅官网

@SentinelResource 注解

Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandlerfallback 函数来进行限流之后的处理。@SentinelResource 注解是 Sentinel 提供的最重要的注解之一,它还包含了多个属性,如下表:

属性 说明 必填与否 使用要求
value 用于指定资源的名称 必填 -
entryType entry 类型 可选项(默认为 EntryType.OUT) -
blockHandler 服务限流后会抛出 BlockException 异常,而 blockHandler 则是用来指定一个函数来处理 BlockException 异常的。 简单点说,该属性用于指定服务限流后的后续处理逻辑。 可选项
  1. blockHandler 函数访问范围需要是 public
  2. 返回类型需要与原方法相匹配;
  3. 参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException;
  4. blockHandler 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 blockHandler 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
blockHandlerClass 若 blockHandler 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 可选项
  1. 不能单独使用,必须与 blockHandler 属性配合使用;
  2. 该属性指定的类中的 blockHandler 函数必须为 static 函数,否则无法解析。
fallback 用于在抛出异常(包括 BlockException)时,提供 fallback 处理逻辑。 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 可选项
  1. 返回值类型必须与原函数返回值类型一致
  2. 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常
  3. fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallbackClass 若 fallback 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。 可选项
  1. 不能单独使用,必须与 fallback 或 defaultFallback 属性配合使用
  2. 该属性指定的类中的 fallback 函数必须为 static 函数,否则无法解析。
defaultFallback 默认的 fallback 函数名称,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。 默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 可选项
  1. 返回值类型必须与原函数返回值类型一致
  2. 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
  3. defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore 用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 可选项 -

注:在 Sentinel 1.6.0 之前,fallback 函数只针对降级异常(DegradeException)进行处理,不能处理业务异常。

让我们再回顾一下之前的例子

    @GetMapping("/register")@SentinelResource("register")public CommonResult<String> register() {userService.register();return ResultUtils.success();}

前面我们用到的@SentinelResource注解就是定义资源了,服务启动并被调用过该接口后(Sentinel懒加载)就可以再控制台看到对应的资源,我们就可以对其添加对应的规则了

注意:注解方式埋点不支持 private 方法。

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallbackdefaultFallback,则被限流降级时会将 BlockException 直接抛出

代码示例:

public class TestService {// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})public void test() {System.out.println("Test");}// 原函数@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")public String hello(long s) {return String.format("Hello at %d", s);}// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.public String helloFallback(long s) {return String.format("Halooooo %d", s);}// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.public String exceptionHandler(long s, BlockException ex) {// Do some log here.ex.printStackTrace();return "Oops, error occurred at " + s;}
}

Sentinel自定义限流异常处理

系统有默认的异常页面,但是该异常页面对用户不友好,我们应该尽量避免直接返回系统默认的异常页面,而需要根据自己的需求自定义对应的异常处理逻辑

方法级别

我们可以自定义通用的限流处理逻辑,然后在@SentinelResource中指定。

/*** @author Pymjl* @version 1.0* @date 2022/8/25 12:48**/
@RestController
@RequestMapping("/user")
@Log4j2
public class UserController {@ResourceUserService userService;@Value("${server.port}")private String port;@GetMapping("/test")@SentinelResource(value = "test", blockHandler = "handleTest")public CommonResult<String> test(HttpServletRequest request) throws UnknownHostException {System.out.printf("被[/%s:%s]调用了一次%n", request.getRemoteHost(), request.getRemotePort());String hostAddress = InetAddress.getLocalHost().getHostAddress() + ":" + port;return ResultUtils.success(hostAddress);}@GetMapping("/get/{id}")@SentinelResource(value = "getUser")public CommonResult<User> get(@PathVariable("id") Long id) {return ResultUtils.success(userService.get(id));}public CommonResult<String> handleTest(HttpServletRequest request, BlockException blockException) {log.error("调用/user/test失败");return ResultUtils.fail("Sentinel流控,调用失败");}}

通过@SentinelResource的blockhandler属性指定对应的异常处理逻辑

注意:

  • blockHandler 函数访问范围需要是 public
  • 返回类型需要与原方法相匹配
  • 参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
  • blockHandler 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 blockHandler 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析

当抛出异常时,若注解指定了异常的处理逻辑那么就直接使用指定的逻辑。若未指定,使用全局的异常处理,否则就返回默认的异常页面

全局异常处理

创建一个全局处理的类,然后实现BlockExceptionHandler

package cuit.epoch.pymjl.handler;import cn.hutool.json.JSONUtil;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import cuit.epoch.pymjl.result.CommonResult;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** @author Pymjl* @version 1.0* @date 2022/9/4 19:57**/
@Component
@Log4j2
public class MyBlockExceptionHandler implements BlockExceptionHandler {@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e)throws Exception {//getRule返回资源、规则的详细信息log.error("BlockExceptionHandler BlockException================" + e.getRule());CommonResult<String> result = new CommonResult<>();result.setSucceed(false);if (e instanceof FlowException) {result.setMessage("接口被限流了");} else if (e instanceof DegradeException) {result.setMessage("服务降级了");} else if (e instanceof ParamFlowException) {result.setMessage("热点参数被限流了");} else if (e instanceof AuthorityException) {result.setMessage("授权规则不通过");}//返回Json数据httpServletResponse.setStatus(500);httpServletResponse.setCharacterEncoding("UTF-8");httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);try (PrintWriter writer = httpServletResponse.getWriter()) {writer.write(JSONUtil.toJsonPrettyStr(result));writer.flush();} catch (IOException ioException) {log.error("异常:{}", ioException.toString());}}
}

测试

我们启动服务,然后将对应的资源初始化再Sentinel的控制台,然后添加对应的流控规则

访问接口,对应的接口,观察结果

我们可以看到,因为test这个资源指定了对应的异常处理逻辑,所以返回的是handleTest中的处理逻辑

而因为/user/get/1 并未指定,所以就走的全局异常处理逻辑

遇见的bug

注:我这里遇见几个问题,因为水品有限,暂时还不知道这种情况的原因:

  1. 当给URL配置流控规则时,blockhandler指定的异常处理逻辑失效,如图:

对应的代码:

/*** @author Pymjl* @version 1.0* @date 2022/8/25 12:48**/
@RestController
@RequestMapping("/user")
@Log4j2
public class UserController {@ResourceUserService userService;@Value("${server.port}")private String port;@GetMapping("/test")@SentinelResource(value = "test", blockHandler = "handleTest")public CommonResult<String> test(HttpServletRequest request) throws UnknownHostException {System.out.printf("被[/%s:%s]调用了一次%n", request.getRemoteHost(), request.getRemotePort());String hostAddress = InetAddress.getLocalHost().getHostAddress() + ":" + port;return ResultUtils.success(hostAddress);}public CommonResult<String> handleTest(HttpServletRequest request, BlockException blockException) {log.error("调用/user/test失败");return ResultUtils.fail("Sentinel流控,调用失败");}}

并没有走指定的异常处理逻辑,而是进入了全局的限流异常处理。当给对应的资源test 添加对应的流控规则时解决该问题

  1. 当给未指定限流处理逻辑的资源添加对应的流控规则时,抛出的时fallback异常

对应的代码

    @GetMapping("/get/{id}")@SentinelResource(value = "getUser")public CommonResult<User> get(@PathVariable("id") Long id) {return ResultUtils.success(userService.get(id));}

返回的却是fallback异常,按照我的理解应该是走限流的全局异常处理才是

如果有同学知道原因还请指正

项目源码:gitee github

get(@PathVariable(“id”) Long id) {
return ResultUtils.success(userService.get(id));
}


[外链图片转存中...(img-EDtQfWvU-1662298885385)]返回的却是fallback异常,按照我的理解应该是走限流的全局异常处理才是> 如果有同学知道原因还请指正项目源码:[gitee](https://gitee.com/pymjl_0/cloud-learn)    [github](https://github.com/Pymjl/cloud-learn)

@SentinelResource详解相关推荐

  1. Sentinel @SentinelResource 详解

    Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源.处理 BlockException 等.使用 Sentinel Ann ...

  2. @SentinelResource 注解详解

    @SentinelResource 注解详解 引入 pom 依赖 @SentinelResource 注解 引入 pom 依赖 官网说明: Sentinel 提供了 @SentinelResource ...

  3. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  4. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

  5. docker常用命令详解

    docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...

  6. 通俗易懂word2vec详解词嵌入-深度学习

    https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...

  7. 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法

    深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...

  8. CUDA之nvidia-smi命令详解---gpu

    nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...

  9. Bert代码详解(一)重点详细

    这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...

最新文章

  1. NetBeans 时事通讯(刊号 # 117 - Sep 16, 2010)
  2. jdbc链接数据库mysql
  3. 7-8 数字三角形 (31 分)(思路+详解+动态规划)Come Baby!!!!!!!!!!!
  4. storm mysql druid_Druid 集成
  5. loadrunner学习记录一
  6. [转]将微信和支付宝支付的个二维码合二为一
  7. python中pop函数的用法_python中pop()函数怎么用
  8. 人工智能运行环境linux,Intel OpenVINO 人工智能推论环境搭建 (Linux) 第一章
  9. 调整样式_“寒来暑往”美国海军陆战队应季节调整迷彩样式的规定变化
  10. 高效Redis Client多线程操作的并发吞吐设计
  11. 【风电功率预测】基于matlab BP神经网络风电功率预测【含Matlab源码 399期】
  12. WinDbg 入门教程
  13. 网络流量分析与Android逆向小结
  14. mysql 的一个错误 Error Code: 2013. Lost connection to MySQL server during...
  15. NetCDF数据处理
  16. MQTT的学习之Mosquitto简要教程(安装使用)
  17. 计算机丢失softwareinspect,logo1 systemInspect山泉查不到。。。和顶的又不一样[求助】...
  18. 百度云盘archlinux manjaro直接安装
  19. Java实现调用默认浏览器打开网址
  20. 测试过程中遇到的那些奇葩bug

热门文章

  1. Java使用华软通信能力平台实现短信发送
  2. halcon介绍以及与opencv比较
  3. [论文笔记]Beyond Part Models: Person Retrieval with Refined Part Pooling(PCB)
  4. 数理统计之协方差矩阵
  5. 微信小程序入门10-微信公众号token验证失败
  6. N1文法 ーー 可能・不可能・禁止
  7. 2020,AI创业与投资进入“深水区”
  8. Linux学习-67-日志服务器设置和日志分析工具(logwatch)安装及使用
  9. 常见的RC是什么意思
  10. ECharts2.0