Sentinel 流量防卫兵-SpringCloudAlibaba No.4
Posted Fire king
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sentinel 流量防卫兵-SpringCloudAlibaba No.4相关的知识,希望对你有一定的参考价值。
这里写目录标题
安装与部署
1.下载
2.配置idea启动
启动之后,就可以访问到Sentinel的监控页面了,用户名和密码都是sentinel
,地址:http://localhost:8858/#/dashboard
3.需要的微服务加上依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
4.需要的微服务的配置文件
注意对齐方式
spring:
application:
name: userservice
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
# 添加监控页面地址即可
dashboard: localhost:8858
其实重点是这段:
sentinel:
transport:
# 添加监控页面地址即可
dashboard: localhost:8858
现在启动我们的服务,然后访问一次服务,这样Sentinel中就会存在信息了(懒加载机制,不会一上来就加载):
流量控制
1.小试牛刀
打开管理页面的簇点链路模块:
这里演示对我们的借阅接口进行限流,点击流控
,会看到让我们添加流控规则:
- 阈值类型:QPS就是每秒钟的请求数量,并发线程数是按服务当前使用的线程数据进行统计的。
- 流控模式:当达到阈值时,流控的对象,这里暂时只用直接。
- 流控效果:就是我们上面所说的三种方案。
这里我们选择QPS
、阈值设定为1
,流控模式选择直接
、流控效果选择快速失败
,可以看到,当我们快速地进行请求时,会直接返回失败信息:
2.链路模式:
1.一视同仁
我们可以对某一个方法进行限流控制,无论是谁在何处调用了它,这里需要使用到@SentinelResource
,一旦方法被标注,那么就会进行监控,比如我们这里创建两个请求映射,都来调用Service的被监控方法:
@RestController
public class BorrowController
@Resource
BorrowService service;
@RequestMapping("/borrow/uid")
UserBorrowDetail findUserBorrows(@PathVariable("uid") int uid)
return service.getUserBorrowDetailByUid(uid);
@RequestMapping("/borrow2/uid")
UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid)
return service.getUserBorrowDetailByUid(uid);
@SentinelResource("getBorrow")
中的getBorrow
可以看作是getUserBorrowDetailByUid
方法的资源名。
@Service
public class BorrowServiceImpl implements BorrowService
@Resource
BorrowMapper mapper;
@Resource
UserClient userClient;
@Resource
BookClient bookClient;
@Override
@SentinelResource("getBorrow") //监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称,这个注解可以加在任何方法上,包括Controller中的请求映射方法,跟HystrixCommand贼像
public UserBorrowDetail getUserBorrowDetailByUid(int uid)
List<Borrow> borrow = mapper.getBorrowsByUid(uid);
User user = userClient.getUserById(uid);
List<Book> bookList = borrow
.stream()
.map(b -> bookClient.getBookById(b.getBid()))
.collect(Collectors.toList());
return new UserBorrowDetail(user, bookList);
spring:
application:
name: borrowservice
cloud:
sentinel:
transport:
dashboard: localhost:8858
# 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
web-context-unify: false
然后我们在Sentinel控制台中添加流控规则,注意是针对此方法,可以看到已经自动识别到borrow接口下调用了这个方法:
最后我们在浏览器中对这两个接口都进行测试,会发现,无论请求哪个接口,只要调用了Service中的getUserBorrowDetailByUid
这个方法,都会被限流。注意限流的形式是后台直接抛出异常,至于怎么处理我们后面再说。
2.区别对待
那么这个链路选项实际上就是决定只限流从哪个方向来的调用,比如我们只对borrow2
这个接口对getUserBorrowDetailByUid
方法的调用进行限流,那么我们就可以为其指定链路:
然后我们会发现,限流效果只对我们配置的链路接口有效,而其他链路是不会被限流的。
除了直接对接口进行限流规则控制之外,我们也可以根据当前系统的资源使用情况,决定是否进行限流:
3.其他流控模式刻自行参悟
限流和异常处理
1.controller
@RequestMapping("/blocked")
JSONObject blocked()
JSONObject object = new JSONObject();
object.put("code", 403);
object.put("success", false);
object.put("massage", "您的请求频率过快,请稍后再试!");
return object;
2.配置文件
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8858
# 将刚刚编写的请求映射设定为限流页面
block-page: /blocked
这样,当被限流时,就会被重定向到指定页面:
3.更加细粒度限流异常处理-方法级别
1.需要限流的方法
@Override
@SentinelResource(value = "getBorrow", blockHandler = "blocked") //指定blockHandler,也就是被限流之后的替代解决方案,这样就不会使用默认的抛出异常的形式了
public UserBorrowDetail getUserBorrowDetailByUid(int uid)
List<Borrow> borrow = mapper.getBorrowsByUid(uid);
User user = userClient.getUserById(uid);
List<Book> bookList = borrow
.stream()
.map(b -> bookClient.getBookById(b.getBid()))
.collect(Collectors.toList());
return new UserBorrowDetail(user, bookList);
//替代方案,注意参数和返回值需要保持一致,并且参数最后还需要额外添加一个BlockException
public UserBorrowDetail blocked(int uid, BlockException e)
return new UserBorrowDetail(null, Collections.emptyList());
可以看到,一旦被限流将执行替代方案,最后返回的结果就是:
2.其他异常处理
注意blockHandler
只能处理限流情况下抛出的异常,包括下面即将要介绍的热点参数限流也是同理,如果是方法本身抛出的其他类型异常,不在管控范围内,但是可以通过其他参数进行处理:
@RequestMapping("/test")
@SentinelResource(value = "test",
fallback = "except", //fallback指定出现异常时的替代方案
exceptionsToIgnore = IOException.class) //忽略那些异常,也就是说这些异常出现时不使用替代方案
String test()
throw new RuntimeException("HelloWorld!");
//替代方法必须和原方法返回值和参数一致,最后可以添加一个Throwable作为参数接受异常
String except(Throwable t)
return t.getMessage();
这样,其他的异常也可以有替代方案了:
特别注意这种方式会在没有配置blockHandler
的情况下,将Sentinel机制内(也就是限流的异常)的异常也一并处理了,如果配置了blockHandler
,那么在出现限流时,依然只会执行blockHandler
指定的替代方案(因为限流是在方法执行之前进行的)
另外,实践领悟:
- contoller级别会覆盖方法级别限流代替方案
- 方法级别代替方案,如果对controller级别设置流控规则,出现的是默认代替方案而不是自定义的
4.热点参数限流
我们还可以对某一热点数据进行精准限流,比如在某一时刻,不同参数被携带访问的频率是不一样的:
- http://localhost:8301/test?a=10 访问100次
- http://localhost:8301/test?b=10 访问0次
- http://localhost:8301/test?c=10 访问3次
由于携带参数a
的请求比较多,我们就可以只对携带参数a
的请求进行限流。
这里我们创建一个新的测试请求映射:数限流
1.controller
@RequestMapping("/test")
@SentinelResource("test") //注意这里需要添加@SentinelResource才可以,用户资源名称就使用这里定义的资源名称
String findUserBorrows2(@RequestParam(value = "a", required = false) int a,
@RequestParam(value = "b", required = false) int b,
@RequestParam(value = "c",required = false) int c)
return "请求成功!a = "+a+", b = "+b+", c = "+c;
然后开始访问我们的测试接口,可以看到在携带参数a时,当访问频率超过设定值,就会直接被限流,这里是直接在后台抛出异常:
而我们使用其他参数或是不带a
参数,那么就不会出现这种问题了:
除了直接对某个参数精准限流外,我们还可以对参数携带的指定值单独设定阈值,比如我们现在不仅希望对参数a
限流,而且还希望当参数a
的值为10时,QPS达到5再进行限流,那么就可以设定例外:
这样,当请求携带参数a
,且参数a
的值为10时,阈值将按照我们指定的特例进行计算。
服务熔断和降级
1.Sentinel中如何进行熔断和降级操作
打开管理页面,我们可以自由新增熔断规则:
其中,熔断策略有三种模式:
2.慢调用比例:
如果出现那种半天都处理不完的调用,有可能就是服务出现故障,导致卡顿,这个选项是按照最大响应时间(RT)进行判定,如果一次请求的处理时间超过了指定的RT,那么就被判定为慢调用
,在一个统计时长内,如果请求数目大于最小请求数目,并且被判定为慢调用
的请求比例已经超过阈值,将触发熔断。经过熔断时长之后,将会进入到半开状态进行试探(这里和Hystrix一致)
然后修改一下接口的执行,我们模拟一下慢调用:
@RequestMapping("/borrow2/uid")
UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid) throws InterruptedException
Thread.sleep(1000);
return null;
重启,然后我们创建一个新的熔断规则:
可以看到,超时直接触发了熔断,进入到阻止页面:
3.异常比例:
这个与慢调用比例类似,不过这里判断的是出现异常的次数,与上面一样,我们也来进行一些小测试:
@RequestMapping("/borrow2/uid")
UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid)
throw new RuntimeException();
启动服务器,接着添加我们的熔断规则:
现在我们进行访问,会发现后台疯狂报错,然后就熔断了:
4.异常数:
这个和上面的唯一区别就是,只要达到指定的异常数量,就熔断,这里我们修改一下熔断规则:
现在我们再次不断访问此接口,可以发现,效果跟之前其实是差不多的,只是判断的策略稍微不同罢了:
Feign支持Sentinel
1.需要的服务在配置文件中的配置
使用Hystrix的时候,就可以直接对Feign的每个接口调用单独进行服务降级,而使用Sentinel,也是可以的,首先我们需要在配置文件中开启支持:
feign:
sentinel:
enabled: true
2.创建实现类
之后的步骤其实和之前是一模一样的,首先创建实现类:
@Component
public class UserClientFallback implements UserClient
@Override
public User getUserById(int uid)
User user = new User();
user.setName("我是替代方案");
return user;
然后直接启动就可以了,中途的时候我们吧用户服务全部下掉,可以看到正常使用替代方案:
这样Feign的配置就OK了,那么传统的RestTemplate呢?我们可以使用@SentinelRestTemplate
注解实现:
以上是关于Sentinel 流量防卫兵-SpringCloudAlibaba No.4的主要内容,如果未能解决你的问题,请参考以下文章
Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵基础实战
Sentinel 流量防卫兵-SpringCloudAlibaba No.4
#私藏项目实操分享#Alibaba中间件技术系列「Sentinel技术专题」分布式系统的流量防卫兵的基本介绍(入门源码介绍)