Sentinel自定义流控兜底,异常兜底,链路失效,踩坑总结~
想一口吃个胖子是不可能了,本来想快速过一遍alibaba,做个项目熟悉一下再深入研究,搞到Sentinel时遇到一些问题。
首先是流控模式中的链路模式时效问题,无法正确的再控制台显示和流控。已经通过 https://github.com/alibaba/Sentinel/issues/1213 改好了。亲测可用,我的 sentinel-1.7.1,项目client里用的2.2.0。
下班了,不写了,明天再写哈哈哈。
上班了,继续!
先说链路的问题。
众所周知懒加载的sentinel dashboard 上面会展示调用过的接口信息,然后链路绑定时展示不全,如下
Controller中有两个接口,他们同时调用了Service的方法
@Autowired private TestService testService; @GetMapping("/link1") public ResponseEntity<String> link1() { return ResponseEntity.ok( String.format("link1,调用test,结果为%s", testService.hello())); } @GetMapping("/link2") public ResponseEntity<String> lin2() { return ResponseEntity.ok( String.format("link2,调用test,结果为%s", testService.hello())); }
又将hello方法注册到了sentinel中,这里先不看兜底的方法问题,先解决了这个链路限流失败的问题。
@Service public class TestService { @SentinelResource(value = "hello",
blockHandlerClass = SentinelHandlersClass.class, blockHandler = "handleException",
fallbackClass = SentinelHandlersClass.class,fallback = "handleError") public String hello() { return "hello"; } }
这样调用在sentinel中显示为
很明显没有展示全,这就导致了后面一列的问题发生了,使用链路限流会失效的问题。
解决方法从github上看过了,在这里记录一下
导包
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-web-servlet</artifactId> </dependency>
至于为什么这么写还没有研究,待更....
@Configuration public class FilterContextConfigSentinel { /** * @NOTE 在spring-cloud-alibaba v2.1.1.RELEASE及前,sentinel1.7.0及后,关闭URL PATH聚合需要通过该方式,spring-cloud-alibaba v2.1.1.RELEASE后,可以通过配置关闭:spring.cloud.sentinel.web-context-unify=false * 手动注入Sentinel的过滤器,关闭Sentinel注入CommonFilter实例,修改配置文件中的 spring.cloud.sentinel.filter.enabled=false * 入口资源聚合问题:https://github.com/alibaba/Sentinel/issues/1024 或 https://github.com/alibaba/Sentinel/issues/1213 * 入口资源聚合问题解决:https://github.com/alibaba/Sentinel/pull/1111 */ @Bean public FilterRegistrationBean sentinelFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new CommonFilter()); registration.addUrlPatterns("/*"); // 入口资源关闭聚合 registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false"); registration.setName("sentinelFilter"); registration.setOrder(1); return registration; } }
我们重启测试一下
只要链路能正常展示了又搞了一下链路限流完全ok了啊。
下面开始说兜底的事。一点一点屡。
先贴一下兜底的代码
注意
1.兜底方法的参数要和被的兜底方法的一样
2.返回值要和被兜底方法的返回值一样
3.限流兜底方法的 BlockException blockException 必须要有,异常兜底的 Throwable blockException 参数好像不是必须,但是没写他也没找到异常兜底。
4.方法必须是public static 修饰。
public class SentinelHandlersClass
{ //限流时的兜底处理 public static ResponseEntity<String> handleException(BlockException blockException) { return ResponseEntity.ok("兜底了"); } //异常兜底 public static ResponseEntity<String> handleError(Throwable blockException) { return ResponseEntity.ok("兜底了,异常"); } }
@SentinelResource(value = "link1",
blockHandlerClass = SentinelHandlersClass.class, //限流兜底方法所在类
blockHandler = "handleException", //限流时兜底方法
fallbackClass = SentinelHandlersClass.class, //异常兜底放方法所在类
fallback = "handleError") //异常时兜底
大概解释了一下,我们测试一下,目前是这样配置的
然后他展示的是这样,感觉乱七八糟的,
代码里/link1的 @SentinelResource 注解有value属性,那就是他在sentinel的资源名,而/link2没有所以给了个默认,然后测试了一下各个资源名的流控都没问题。问题是都不走兜底处理,只有link1走了流控,连/link1的资源都不走,再吐槽一下/link2的资源名默认给的那个,完全错误了,回头看一下源码。
以上三个都不走兜底处理,再展示一下link1。
当一秒内访问超过两次走兜底方法,而不是默认的429异常了。
然后再展示一种情况,有关于这个兜底的问题文章不是很多,有网友说把@RequestMapping和资源名@SentinelResource的value设置成一样的,我们也试一下。
然后我们来做限流,直接给第二个/link1做。神奇的事情发生了,/link1接口会直接返回兜底内容,限流以后又返回了默认的429错误。
如图
这就是亲爱的网友给大家留的坑,可能他们在抄博客的时候也没动什么脑子,给萌新留坑,污染环境。另外不吐槽原创了,可能版本不一样,我的版本贴在文章开头了。
所以找不到兜底方法或者不执行兜底方法的问题不在乎接口名和资源名是否相同。改回来以后就没问题了。需要注意要给资源名做限流,不要给默认的做限流,这样还是会找不到兜底处理。
异常兜底一样。
现在再测试一下下层的hello方法,给他做限流会返回什么呢。
这里做了两个限流,一个是直接限流hello,另一个是做了链路限流,如图,当hello超过访问量限制link1的访问
测试以后也和普通的一层接口不同,两个限流都是直接走异常兜底的。
结束!再见