|
本文参考孙卫琴,杜聚宾所创作的<<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。
图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。
图2 为enter资源设定流控规则
通过浏览器访问消费者微服务,URL为http://localhost:8082/enter/Tom,不断刷新网页,有时会出现图3所示的错误提示页面。该页面的信息是由handleBlock()方法返回的。
图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的错误页面:
图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()方法返回的。
图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
|
网站系统异常
系统异常信息 |
Request URL:
http://www.javathinker.net/WEB-INF/lybbs/jsp/topic.jsp?postID=4209
java.lang.NullPointerException
如果你不知道错误发生的原因,请把上面完整的信息提交给本站管理人员。
|
|