前面我们完成了对Sentinel的搭建与连接,接着我们来看看Sentinel的第一个功能,流量控制。
我们的机器不可能无限制的接受和处理客户端的请求,如果不加以限制,当发生高并发情况时,系统资源将很快被耗尽。为了避免这种情况,我们就可以添加流量控制(也可以说是限流)当一段时间内的流量到达一定的阈值的时候,新的请求将不再进行处理,这样不仅可以合理地应对高并发请求,同时也能在一定程度上保护服务器不受到外界的恶意攻击。
那么要实现限流,正常情况下,我们该采取什么样的策略呢?
针对于是否超过流量阈值的判断,这里我们提4种算法:
14:15
到14:16
这一分钟内,请求量不能超过100
,也就是一分钟之内不能超过100
次请求,那么就可以像下面这样进行划分: 出现上面这种情况,符合固定时间窗口算法的规则,所以这200个请求都能正常接受,但是,如果你反应比较快,应该发现了,我们其实希望的是60秒内只有100个请求,但是这种情况却是在3秒内出现了200个请求,很明显已经违背了我们的初衷。
因此,当遇到临界点时,固定时间窗口算法存在安全隐患。
好了,了解完了我们的限流策略和判定方法之后,我们在Sentinel中进行实际测试一下,打开管理页面的簇点链路模块:
这里演示对我们的借阅接口进行限流,点击流控
,会看到让我们添加流控规则:
这里我们选择QPS
、阈值设定为1
,流控模式选择直接
、流控效果选择快速失败
,可以看到,当我们快速地进行请求时,会直接返回失败信息:
这里各位最好自行尝试一下其他的流控效果,熟悉和加深印象。
最后我们来看看这些流控模式有什么区别:
我们首先来看看关联,比如现在我们对自带的/error
接口进行限流:
注意限流是作用于关联资源的,一旦发现关联资源超过阈值,那么就会对当前的资源进行限流,我们现在来测试一下,这里使用PostMan的Runner连续对关联资源发起请求:
开启Postman,然后我们会发现借阅服务已经凉凉:
当我们关闭掉Postman的任务后,恢复正常。
最后我们来讲解一下链路模式,它能够更加精准的进行流量控制,链路流控模式指的是,当从指定接口过来的资源请求达到限流条件时,开启限流,这里得先讲解一下@SentinelResource
的使用。
我们可以对某一个方法进行限流控制,无论是谁在何处调用了它,这里需要使用到@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);
- }
- }
- @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 = mapper.getBorrowsByUid(uid); - User user = userClient.getUserById(uid);
- List
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
这个方法,都会被限流。注意限流的形式是后台直接抛出异常,至于怎么处理我们后面再说。
那么这个链路选项实际上就是决定只限流从哪个方向来的调用,比如我们只对borrow2
这个接口对getUserBorrowDetailByUid
方法的调用进行限流,那么我们就可以为其指定链路:
然后我们会发现,限流效果只对我们配置的链路接口有效,而其他链路是不会被限流的。
除了直接对接口进行限流规则控制之外,我们也可以根据当前系统的资源使用情况,决定是否进行限流:
系统规则支持以下的模式:
maxQps * minRt
估算得出。设定参考值一般是 CPU cores * 2.5
。
这里就不进行演示了。