• Sentinel流量控制


    前面我们完成了对Sentinel的搭建与连接,接着我们来看看Sentinel的第一个功能,流量控制。

    我们的机器不可能无限制的接受和处理客户端的请求,如果不加以限制,当发生高并发情况时,系统资源将很快被耗尽。为了避免这种情况,我们就可以添加流量控制(也可以说是限流)当一段时间内的流量到达一定的阈值的时候,新的请求将不再进行处理,这样不仅可以合理地应对高并发请求,同时也能在一定程度上保护服务器不受到外界的恶意攻击。

    那么要实现限流,正常情况下,我们该采取什么样的策略呢?

    • 方案一:快速拒绝,既然不再接受新的请求,那么我们可以直接返回一个拒绝信息,告诉用户访问频率过高。
    • 方案二:预热,依然基于方案一,但是由于某些情况下高并发请求是在某一时刻突然到来,我们可以缓慢地将阈值提高到指定阈值,形成一个缓冲保护。
    • 方案三:排队等待,不接受新的请求,但是也不直接拒绝,而是进队列先等一下,如果规定时间内能够执行,那么就执行,要是超时就算了。

    针对于是否超过流量阈值的判断,这里我们提4种算法:

    1. 漏桶算法
      顾名思义,就像一个桶开了一个小孔,水流进桶中的速度肯定是远大于水流出桶的速度的,这也是最简单的一种限流思路:


      我们知道,桶是有容量的,所以当桶的容量已满时,就装不下水了,这时就只有丢弃请求了。
      利用这种思想,我们就可以写出一个简单的限流算法。
    1. 令牌桶算法
      只能说有点像信号量机制。现在有一个令牌桶,这个桶是专门存放令牌的,每隔一段时间就向桶中丢入一个令牌(速度由我们指定)当新的请求到达时,将从桶中删除令牌,接着请求就可以通过并给到服务,但是如果桶中的令牌数量不足,那么不会删除令牌,而是让此数据包等待。


      可以试想一下,当流量下降时,令牌桶中的令牌会逐渐积累,这样如果突然出现高并发,那么就能在短时间内拿到大量的令牌。
    1. 固定时间窗口算法
      我们可以对某一个时间段内的请求进行统计和计数,比如在14:1514:16这一分钟内,请求量不能超过100,也就是一分钟之内不能超过100次请求,那么就可以像下面这样进行划分:


      虽然这种模式看似比较合理,但是试想一下这种情况:
      • 14:15:59的时候来了100个请求
      • 14:16:01的时候又来了100个请求

    出现上面这种情况,符合固定时间窗口算法的规则,所以这200个请求都能正常接受,但是,如果你反应比较快,应该发现了,我们其实希望的是60秒内只有100个请求,但是这种情况却是在3秒内出现了200个请求,很明显已经违背了我们的初衷。
    因此,当遇到临界点时,固定时间窗口算法存在安全隐患。

    1. 滑动时间窗口算法
      相对于固定窗口算法,滑动时间窗口算法更加灵活,它会动态移动窗口,重新进行计算:


      虽然这样能够避免固定时间窗口的临界问题,但是这样显然是比固定窗口更加耗时的。

    好了,了解完了我们的限流策略和判定方法之后,我们在Sentinel中进行实际测试一下,打开管理页面的簇点链路模块:

    这里演示对我们的借阅接口进行限流,点击流控,会看到让我们添加流控规则:

    • 阈值类型:QPS就是每秒钟的请求数量,并发线程数是按服务当前使用的线程数据进行统计的。
    • 流控模式:当达到阈值时,流控的对象,这里暂时只用直接。
    • 流控效果:就是我们上面所说的三种方案。

    这里我们选择QPS、阈值设定为1,流控模式选择直接、流控效果选择快速失败,可以看到,当我们快速地进行请求时,会直接返回失败信息:

    这里各位最好自行尝试一下其他的流控效果,熟悉和加深印象。

    最后我们来看看这些流控模式有什么区别:

    • 直接:只针对于当前接口。
    • 关联:当其他接口超过阈值时,会导致当前接口被限流。
    • 链路:更细粒度的限流,能精确到具体的方法。

    我们首先来看看关联,比如现在我们对自带的/error接口进行限流:

    注意限流是作用于关联资源的,一旦发现关联资源超过阈值,那么就会对当前的资源进行限流,我们现在来测试一下,这里使用PostMan的Runner连续对关联资源发起请求:

    开启Postman,然后我们会发现借阅服务已经凉凉:

    当我们关闭掉Postman的任务后,恢复正常。

    最后我们来讲解一下链路模式,它能够更加精准的进行流量控制,链路流控模式指的是,当从指定接口过来的资源请求达到限流条件时,开启限流,这里得先讲解一下@SentinelResource的使用。

    我们可以对某一个方法进行限流控制,无论是谁在何处调用了它,这里需要使用到@SentinelResource,一旦方法被标注,那么就会进行监控,比如我们这里创建两个请求映射,都来调用Service的被监控方法:

    1. @RestController
    2. public class BorrowController {
    3. @Resource
    4. BorrowService service;
    5. @RequestMapping("/borrow/{uid}")
    6. UserBorrowDetail findUserBorrows(@PathVariable("uid") int uid){
    7. return service.getUserBorrowDetailByUid(uid);
    8. }
    9. @RequestMapping("/borrow2/{uid}")
    10. UserBorrowDetail findUserBorrows2(@PathVariable("uid") int uid){
    11. return service.getUserBorrowDetailByUid(uid);
    12. }
    13. }

    1. @Service
    2. public class BorrowServiceImpl implements BorrowService{
    3. @Resource
    4. BorrowMapper mapper;
    5. @Resource
    6. UserClient userClient;
    7. @Resource
    8. BookClient bookClient;
    9. @Override
    10. @SentinelResource("getBorrow") //监控此方法,无论被谁执行都在监控范围内,这里给的value是自定义名称,这个注解可以加在任何方法上,包括Controller中的请求映射方法,跟HystrixCommand贼像
    11. public UserBorrowDetail getUserBorrowDetailByUid(int uid) {
    12. List borrow = mapper.getBorrowsByUid(uid);
    13. User user = userClient.getUserById(uid);
    14. List bookList = borrow
    15. .stream()
    16. .map(b -> bookClient.getBookById(b.getBid()))
    17. .collect(Collectors.toList());
    18. return new UserBorrowDetail(user, bookList);
    19. }
    20. }

    接着添加配置:

    1. spring:
    2. application:
    3. name: borrowservice
    4. cloud:
    5. sentinel:
    6. transport:
    7. dashboard: localhost:8858
    8. # 关闭Context收敛,这样被监控方法可以进行不同链路的单独控制
    9. web-context-unify: false

    然后我们在Sentinel控制台中添加流控规则,注意是针对此方法,可以看到已经自动识别到borrow接口下调用了这个方法:

    最后我们在浏览器中对这两个接口都进行测试,会发现,无论请求哪个接口,只要调用了Service中的getUserBorrowDetailByUid这个方法,都会被限流。注意限流的形式是后台直接抛出异常,至于怎么处理我们后面再说。

    那么这个链路选项实际上就是决定只限流从哪个方向来的调用,比如我们只对borrow2这个接口对getUserBorrowDetailByUid方法的调用进行限流,那么我们就可以为其指定链路:

    然后我们会发现,限流效果只对我们配置的链路接口有效,而其他链路是不会被限流的。

    除了直接对接口进行限流规则控制之外,我们也可以根据当前系统的资源使用情况,决定是否进行限流:

    系统规则支持以下的模式:

    • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
    • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
    • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
    • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
    • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

    这里就不进行演示了。

  • 相关阅读:
    数据库中间件-ShardingSphere-Proxy(一)
    美国费米实验室SQMS启动“量子车库”计划!30+顶尖机构积极参与
    Unity ML-Agents默认接口参数含义
    [源码解析] TensorFlow 之 分布式变量
    (搞定)排序数据结构(1)插入排序 选择排序+冒泡排序
    6.1 ASP.NET Core Web 入门
    Linux---(五)三大工具yum、vim、gcc/g++
    数商云:引领化工业态数字升级,看摩贝如何快速打通全场景互融互通
    Windows平台docker安装redis
    【Flutter】One or more plugins require a higher Android SDK version.
  • 原文地址:https://blog.csdn.net/Leon_Jinhai_Sun/article/details/126068551