>>分享孙卫琴的Java技术专稿和著作 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 8352 个阅读者 刷新本主题
 * 贴子主题:  【Spring Cloud Alibaba专题】@SentinelResource注解的用法 回复文章 点赞(0)  收藏  
作者:sunweiqin    发表时间:2022-08-25 09:44:07     消息  查看  搜索  好友  邮件  复制  引用

本文参考孙卫琴,杜聚宾所创作的<<Spring Cloud Alibaba微服务开发宝典>>一书,即将出版

对于Sentinel,如果还需要针对特定资源定制客户化规则,则可以使用@SentinelResource注解,它有以下属性:
(1)value属性:指定资源的名字。
(2)blockHandler属性:指定当请求被Sentinel拒绝的处理方法。
(3)blockHandlerClass属性:指定当请求被Sentinel拒绝的处理类。
(4)fallback属性:指定响应过程中产生的异常的处理方法。
(5)fallbackClass属性:指定响应过程中产生的异常的处理类。
(6)exceptionsToIgnore:指定需要被忽略的异常。当出现这些异常时,fallback属性以及fallbackClass属性的设定值不起作用。

当控制器类的一个请求处理方法用@SentinelResource注解标识,它就成为Sentinel的资源,可以为它指定客户化的规则,主要包括三个方面:
(1)指定热点规则。针对热点参数进行限流。
(2)指定当请求被Sentinel拒绝的处理方式。
(3)指定在响应过程中产生异常的处理方式。

1.  热点规则

问题:“有一条道路进行交通管制,十分钟内只允许通过一辆车。假如执行紧急灭火任务的一组消防车要通过这条道路,这个规则是否适用呢?”
答案:“应该有所变通,比如允许一分钟内通过五辆消防车。”

同样,对请求进行限流,也会遇到特例。比如某个购物网站的一位VIP用户,频繁光顾网站,大量购物。如果对该用户进行常规限流,就是断了网站的财路。另一方面,对于故意到网站捣乱的信用不良的用户,则需要进行比常规限流更严格的管控。
对于消费者模块,假定资源/enter/{username}的username参数为热点参数,需要对用户Tom和Mike进行专门的限流。

为了配置热点规则,首先在消费者模块的HelloConsumerController类中,为sayHello()方法加上@SentinelResource注解,该注解的value属性把资源名设为enter:

@SentinelResource(value="enter")
@GetMapping(value = "/enter/{username}")
public String sayHello(@PathVariable String username) {……}

接下来在Sentinel控制台的管理页面新增热点规则,参见图1,资源名设为enter,与@SentinelResource注解的value属性值必须一致。
图1的参数索引指定参数位于请求数据中的序号。假定一个资源的URL路径为:/test/{param1}/{param2}。在这个URL路径中有两个参数:param1和param2。其中param1参数的索引为0,param2参数的索引为1。
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
图1  设置热点规则
图1设置的热点规则如下:
(1)普通请求的限流阈值为3,即在统计窗口时长一秒内只允许通过3个请求。
(2)如果请求中索引为0的参数(即第1个参数)的值为Tom,那么限流阈值为20。
(3)如果请求中索引为0的参数的值为Mike,那么限流阈值为1。

由此可见,热点规则允许对不同的热点参数值,设定不同的限流阈值。
通过浏览器访问消费者微服务的以下URL,不断刷新网页,会看到限流的效果不一样:

http://localhost:8080/enter/Jack   //限流阈值为3
http://localhost:8080/enter/Tom    //限流阈值为20
http://localhost:8080/enter/Mike   //限流阈值为1

2.  请求被拒绝的处理

当Sentinel依据特定的规则拒绝一个请求时,会产生BlockException,对这种异常的默认处理方式为返回一个拒绝页面。这种页面带来的用户体验不是很友好。如果需要定制请求被拒绝的处理方式,可以指定@SentinelResource注解的blockHandler属性。

以下代码中的@SentinelResource注解设定了blockHandler属性,它指定由handleBlock()方法来处理请求被拒绝的情况:

@SentinelResource(value="enter",
                      blockHandler="handleBlock")
@GetMapping(value = "/enter/{username}")
public String sayHello(@PathVariable String username) {……}

public String handleBlock(String username,
                              BlockException ex) {
  ex.printStackTrace();
  username+",request is blocked.";
}

handleBlock()方法也在HelloConsumerController类中定义,它的参数和返回类型必须和sayHello()方法相同,此外,还需要增加一个BlockException类型的参数,表示当前的BlockException对象。
在Sentinel控制台为以上@SentinelResource注解所标识的enter资源设定一个流控规则,参见图2。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

图2  为enter资源设定流控规则
通过浏览器访问消费者微服务,URL为http://localhost:8082/enter/Tom,不断刷新网页,有时会出现图3所示的错误提示页面。该页面的信息是由handleBlock()方法返回的。
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
图3  handleBlock()方法返回的错误信息
如果希望在单独的类中处理请求被拒绝的情况,可以设定@SentinelResource注解的blockHandler属性和blockHandlerClass属性:

@SentinelResource(value="enter",
           blockHandler="handleBlock",
           blockHandlerClass=MyBlockHandler.class)
@GetMapping(value = "/enter/{username}")
public String sayHello(@PathVariable String username){……}

以上@SentinelResource注解指定由MyBlockHandler类的handleBlock()方法处理请求被拒绝的情况。例程1是MyBlockHandler类的源代码。

/* 例程1  MyBlockHandler.java */
public class MyBlockHandler {
  public static String handleBlock(String username,
                                    BlockException ex) {
    ex.printStackTrace();
    return username+",request is blocked.";
  }
}

值得注意的是,当以上handleBlock()方法位于单独的MyBlockHandler类中,必须是静态方法。

3.  对异常的处理

Sentinel拒绝请求时会抛出BlockException,为了与这种异常区别,本书把由HelloConsumerController类的sayHello()方法处理业务逻辑抛出的异常称为服务异常。默认情况下,当出现服务异常,会返回如图4的错误页面:
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
图4  sayHello()方法抛出异常时返回的错误页面
以上错误页面带来的用户体验不是很友好。如果需要定制对异常的处理方式,可以设置@SentinelResource注解的fallback属性。
以下代码的@SentinelResource注解设定了fallback属性,它指定由handleException()方法处理异常:

@SentinelResource(value="enter",
                     fallback="handleException")
@GetMapping(value = "/enter/{username}")
public String sayHello(@PathVariable String username) {
  if(username.equals("Monster"))
    throw new IllegalArgumentException(
                                   "Illegal Argument");

  return helloFeignService.sayHello(username);
}

public String handleException(String username,
                                            Throwable ex) {
  ex.printStackTrace();
  return username+",something is wrong.";
}

handleException()方法也在HelloConsumerController类中定义,它的参数和返回类型必须和sayHello()方法相同,此外,还需要增加一个Throwable类型的参数,表示当前产生的异常对象。

如果没有设定@SentinelResource注解的blockHandler属性,那么对于Sentinel拒绝请求而产生的BlockException,也是由fallback属性指定的方法来处理。

通过浏览器访问http://localhost:8082/enter/Monster,就会出现图5所示的错误提示页面。该页面的信息就是由handleException()方法返回的。
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
图5  handleException()方法返回的错误信息
如果希望在单独的类中处理异常,可以设定@SentinelResource注解的fallback属性和fallbackClass属性:

@SentinelResource(value="enter",
          fallback="handleException",
          fallbackClass = MyExceptionHandler.class)
@GetMapping(value = "/enter/{username}")
public String sayHello(@PathVariable String username) {……}

以上@SentinelResource注解指定由MyExceptionHandler类的handleException()方法处理异常。例程2是MyExceptionHandler类的源代码。

/* 例程2  MyExceptionHandler.java */
public class MyExceptionHandler {
  public static String handleException(
                   String username, Throwable ex) {
    ex.printStackTrace();
    return username+",something is wrong.";
  }
}

值得注意的是,当以上handleException()方法位于单独的MyExceptionHandler类中,必须是静态方法。
@SentinelResource注解还有一个exceptionsToIgnore属性,指定所有被忽略的异常,例如:

    @SentinelResource(value="enter",
            blockHandler="handleBlock",
            blockHandlerClass=MyBlockHandler.class,
            fallback="handleException",
            fallbackClass = MyExceptionHandler.class,
            exceptionsToIgnore={NullPointerException.class})

以上代码表明,当出现NullPointerException异常时,MyExceptionHandler类的handleException()方法不会处理该异常。



程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->集合(上)
  JavaWeb开发-->访问数据库(Ⅰ)
  JSP与Hibernate开发-->Java应用分层架构及软件模型
  Java网络编程-->创建非阻塞的HTTP服务器
  精通Spring-->计算属性和数据监听
  Vue3开发-->通过Axios访问服务器
  【Spring Cloud Alibaba专题】OpenFeign开启对请求和响应数据...
  向小伙伴们推荐我所创作前后端编程书籍的学习顺序
  【Spring专题】服务器端推送
  【Spring专题】通过JPA API实现Repository接口
  【Vue专题】Vue3的计算属性实用范例:实现购物车
  【持久化专题】从JPA API中获得Hibernate API
  【Java编程基础专题】用Scanner类读取用户在控制台输入的数据
  【Java网络编程专题】用java.net.URL类访问HTTP服务器读取网...
  【Java网络编程专题】优化访问数据库的程序代码的一些技巧
  【持久化专题】通过JPQL查询语句进行批量处理数据
  【持久化专题】比较JPA的EntityManager接口与Hibernate的Ses...
  【JavaWeb专题】Tomcat与IIS集成详解
  【Java基础编程专题】Java集合的批量操作
  【Java基础编程专题】定时器Timer类的用法
  【Java基础编程专题】Java继承的利弊和使用原则
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。