• Sentinel概述


    前言

    ​ 在现行高并发场景下,通常会采用微服务架构,可以更好的对服务接口进行水平扩展、接口限流等有效保障服务的可用性和稳定性。其中为了保障服务的高可用,同时也为了防止故障蔓延,通常会选择断路器,防止故障的服务还一直占用CPU资源,可能导致其他服务不能及时处理数据请求。

    ​ 由于服务器所能承受的压力是有限的,为了保证可用性,一般会做服务限流,减少服务压力。

    ​ 那么常见的断路器有Sentinel、Hystrix,而常见的微服务限流组件有RateLimiter、Hystrix、Sentinel等。其中Hystrix也在2022年官方发布说明停止维护,且Sentinel是阿里研发,专门为流量控制而生的限流框架。

    ​ 故在微服务中,服务限流熔断建议使用Sentinel。

    介绍

    Sentinel 是一个高可用、高扩展、高稳定性的开源流量控制和熔断降级框架,可以在分布式系统中实现实时的流量控制,防止系统因流量过大导致系统崩溃和服务降级。

    Sentinel 提供了以下功能:

    • 流量控制:通过配置不同的规则,对请求流量进行限制。
    • **熔断降级:**当系统异常情况发生时,可以自动熔断系统,保证系统的可用性。
    • **系统负载保护:**在系统负载高峰期间,可以限制请求流量,避免系统资源耗尽。
    • 实时监控:可以实时监控系统的请求流量、响应时间、错误率等指标。

    ​ Sentinel 面向所有的 Java 应用,可以支持基于 Spring Cloud、Dubbo、gRPC 等服务框架的应用,也可以集成到基于 Tomcat、Jetty 等 Web 容器的应用中。

    什么是流量控制?

    ​ 流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。

    ​ 任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的,由此需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。

    流量控制设计理念

    **1)资源的调用关系。**例如资源的调用链路,资源和资源之间的关系;

    **2)运行指标。**例如 QPS、线程池、系统负载等;

    **3)控制的效果。**例如直接限流、冷启动、排队等

    什么是熔断降级?

    ​ 除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。

    ​ Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

    设计理念

    ​ **Hystrix 通过线程池的方式,来对依赖资源进行了隔离。**这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。

    Sentinel的处理方式:

    **1)通过并发线程数进行限制。**和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。

    **2)通过响应时间对资源进行降级。**当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

    **3)系统负载保护。**Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

    工作机制

    ​ **对于定义需要保护的资源,提供设施对资源进行实时统计和调用链路分析。并根据预设的规则,结合对资源的实时统计信息,对流量进行控制。同时,Sentinel 提供开放的接口,方便定义及改变规则。**另外Sentinel 提供实时的监控系统,可直观了解目前系统的状态。

    使用说明

    1)在Maven中引入Sentinel的依赖:spring-cloud-starter-alibaba-sentinel

    2)配置Sentinel的启动参数:

    # Sentinel 控制台的地址
    spring.cloud.sentinel.transport.dashboard=http://localhost:8080 
    # Sentinel 的启动端口
    spring.cloud.sentinel.transport.port=8719 
    # Sentinel 控制台连接超时时间(ms) spring.cloud.sentinel.transport.dashboard.request-timeout=5000 
    # 配置资源的默认规则 
    spring.cloud.sentinel.rules.defaults[0].grade=QPS spring.cloud.sentinel.rules.defaults[0].count=10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3)若是需要流量控制,添加流量控制注解。

    @SentinelResource(value = "demoMethod", blockHandler = "handleBlock")
    public String demoMethod() {
      return "Hello World";
    }
    // 自定义的响应结果 也可以写成通用类
    public String handleBlock(BlockException ex) {
      return "请求被拦截: " + ex.getClass().getSimpleName();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4)若是需要熔断控制,在@SentinelResource注解中,需要指定fallback属性。

    @SentinelResource(value = "demoMethod", blockHandler = "handleBlock", fallbackClass = DemoServiceFallback.class, fallback = "fallback")
    public String demoMethod() {
      return "Hello World";
    }
    public String handleBlock(BlockException ex) {
      return "请求被拦截: " + ex.getClass().getSimpleName();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5)Sentinel 支持多种多样的流控规则和热点参数限流策略,可以根据业务场景进行灵活配置。

    5-1)QPS 流量控制。

    @RateLimiter(10)

    5-2)线程数流控。

    @ThreadPool(name = “demoMethod”, coreSize = 5, maxQueueSize = 10)

    ​ **5-3)热点参数限流。**可以有效避免因某个参数的恶意使用而导致整个系统崩溃的情况。

    @HotParam(value = “skuId”, mode = ParamFlowItem.FlowControlMode.QPS, threshold = 100)

    优缺点

    优点:

    1)功能丰富,简单便于使用。

    2)提供了多种SPI扩展点,可自定义扩展。

    3)开源免费

    缺点:

    1)仅支持Java

    2)文档略显简单,实践中存在一些坑点需要注意。。

    ​ 坑点,回头补充…

    基本概念

    1、资源

    ​ **资源是 Sentinel 的关键概念。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。**大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。

    2、规则

    ​ 围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

    底层实现原理

    Sentinel 实现流量控制和熔断降级的原理是通过对应用程序进行拦截,然后根据预定义的规则,来判断该请求是否被允许或者需要进行降级处理。

    Sentinel 的拦截器会在应用程序中建立一个责任链,对请求进行逐一拦截。在拦截过程中,Sentinel 会对 Request、Response、Exception 等参数进行统计,根据统计信息来对请求进行熔断或者限流等操作。

    衡量系统稳定性主要有以下三个指标:

    • TPS(Transactions Per Second):每秒钟处理的事务数。
    • RT(Response Time):响应时间,即从发送请求到接收到响应的时间。
    • Error Rate:错误率,即发生错误的请求次数占总请求数的比例。

    ​ Sentinel 根据这三个指标来评估应用程序的健康状况,当这些指标达到某个阈值时,Sentinel 会自动触发相应的流量控制和熔断降级操作。

    @SentinelResource

    ​ 通常使用@SentinelResource来标记一个方法,这个在Sentinel服务中被看成一个Sentinel资源。可以以此切面,查看拦截之后所做的处理。

    ​ 进入SentinelResource切面后,首先会先获取资源的名称、类型,执行SphU.entry方法,并把资源包装成Entry。其在SphU.entry中做限流和熔断的逻辑处理。

    ​ 在SphU.entry方法中的执行流程:

    1)**获取Sentinel上下文(Context)。**就是Sentinel熔断限流执行的上下文,包含资源调用的节点和Entry信息。Context是线程持有的,利用ThreadLocal与当前线程绑定。而Context主要包含Conetxt名称,Node,Entry,这三个类是Sentinel的核心类,提供了资源调用路径、资源调用统计等信息。

    ​ Context是当前线程所持有的Sentinel上下文。进入Sentinel的逻辑时,会首先获取当前线程的Context,如果没有则新建。当任务执行完毕后,会清除当前线程的context。Context 代表调用链路上下文,贯穿一次调用链路中的所有 Entry。

    Context 维持着入口节点(entranceNode)、本次调用链路的 当前节点(curNode)、调用来源(origin)等信息。Context 名称即为调用链路入口名称。

    ​ **Node是对一个@SentinelResource标记的资源的统计包装。**可以通过入口节点的childList,可以追溯资源的调用情况。而每个节点都对应一个@SentinelResource标记的资源及其统计数据,例如:passQps,blockQps,rt等数据。

    Entry是Sentinel中用来表示是否通过限流的一个凭证,如果能正常返回,则说明你可以访问被Sentinel保护的后方服务,否则Sentinel会抛出一个BlockException。

    Entry保存了本次执行entry()方法的一些基本信息,包括资源的Context、Node、对应的责任链等信息,后续完成资源调用后,还需要更具获得的这个Entry去执行一些善后操作,包括退出Entry对应的责任链,完成节点的一些统计信息更新,清除当前线程的Context信息等。

    2)获取资源对应的责任链。

    ​ 默认的责任链中的处理节点包括NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot、FlowSlot、DegradeSlot等。调用链(ProcessorSlotChain)和其中包含的所有Slot都实现了ProcessorSlot接口,采用责任链的模式执行各个节点的处理逻辑,并调用下一个节点。其中,每个资源对应一条单独的责任链。

    ​ 责任链中获取资源,源码中采用双重双检锁方式获取资源。

    3)生成资源调用凭证(Entry)。

    ​ 生成的Entry是CtEntry。其构造参数包括资源包装(ResourceWrapper)、资源对应的责任链以及当前线程的Context。CtEntry是一个双向链表,构建了Sentinel资源的调用链路。

    4)执行责任链中各个节点。

    ​ 责任链和其中的Slot都实现了ProcessorSlot,责任链的entry方法会依次执行责任链各个slot。

    1)**NodeSelectorSlot – 获取当前资源对应Node,构建节点调用树。**这个Node被用于后续资源调用的统计及限流和熔断条件的判断。

    2)ClusterBuilderSlot – 聚合相同资源不同Context的Node,以供后续限流判断使用。默认的限流条件判断就是依据ClusterNode中的统计信息来进行的。

    3)**StatisticSlot – 资源调用统计。**与之前slot不同的是,StatisticSlot的执行时先触发下一个slot的执行,等下面的slot执行完才会执行自己的逻辑。

    4)FlowSlot – 限流判断。之前在StatisticSlot对相关资源调用做的统计,在当前环节使用。默认情况下,限流使用的节点是当前节点的cluster node。主要分析的限流方式是QPS限流。

    ​ 限流操作的核心逻辑–限流规则检查器(FlowRuleChecker):

    • 获取资源对应的限流规则

    • 根据限流规则检查是否被限流

    • 如果被限流,则抛出限流异常FlowException。FlowException继承自BlockException

      限流的关键代码(DefaultController):

    • 获取节点的当前qps计数;

    • 判断获取新的计数后是否超过阈值

    • 超过阈值单返回false,表示被限流,后面会抛出FlowException。否则返回true,不被限流。

    5)Entry.exit()方法,清空资源。执行流程:

    • 判断要退出的entry是否是当前context的当前entry;
    • 如果要退出的entry不是当前context的当前entry,则不退出此entry,而是退出context的的当前entry及其所有父entry,并抛出异常;
    • 如果要退出的entry是当前context的当前entry(这种是正常情况),先退出当前entry对应的责任链的所有slot。在这一步,StatisticSlot会更新node的success计数和RT计数;
    • 将context的当前entry置为被退出的entry的父entry;
    • 如果被退出entry的父entry为空,且context为默认context,自动退出默认context(清除ThreadLocal)。
    • 清除被退出entry的context引用

    ​ 如果触发熔断和限流,会抛出BlockException,我们可以指定blockHandler方法来处理BlockException。而对于业务上的异常,我们也可以配置fallback方法来处理被拦截方法调用产生的异常。

    NodeSelectorSlot的的作用是:

    • 在资源对应的调用链执行时,获取当前context对应的Node,这个Node代表着这个资源的调用情况。
    • 将获取到的node设为当前node,添加到之前的node后面,形成树状的调用路径。(通过Context中的当前Entry进行)
    • 触发下一个Slot的执行。
    小结

    ​ 三大组件Context、Entry、Node,是Sentinel的核心组件,各类信息及资源调用情况都由这三大类持有。

    • 采用责任链模式完成Sentinel的信息统计、熔断、限流等操作;
    • 责任链中NodeSelectSlot负责选择当前资源对应的Node,同时构建node调用树;
    • 责任链中ClusterBuilderSlot负责构建当前Node对应的ClusterNode,用于聚合同一资源对应不同Context的Node;
    • 责任链中的StatisticSlot用于统计当前资源的调用情况,更新Node与其对用的ClusterNode的各种统计数据;
    • 责任链中的FlowSlot根据当前Node对应的ClusterNode(默认)的统计信息进行限流;
    • 资源调用统计数据(例如PassQps)使用滑动时间窗口进行统计;
    • 所有工作执行完毕后,执行退出流程,补充一些统计数据,清理Context。

    Sentinel 高级特性

    1、同时支持同步和异步调用

    ​ Sentinel 可以方便地支持同步和异步调用。 对于同步调用,可以使用 @SentinelResource 注解,在注解中指定需要进行保护的方法,并设置相应的熔断降级、流控规则等限制条件。

    ​ 对于异步调用,则需要使用 Sentinel 提供的异步 Entry 类实现保护。 在使用异步 Entry 进行保护时,需要在异步调用过程中插入 Sentinel 的拦截器,并在异步操作完成后手动释放相应的资源,以便 Sentinel 统计并记录相应的数据。

    CompletableFuture.supplyAsync(() -> {
      Entry entry = null;
      try {
        entry = SphU.asyncEntry("demoMethod");
        // 异步逻辑
        return "Hello World";
      } catch (BlockException ex) {
        return "blocked by Sentinel: " + ex.getClass().getSimpleName();
      } finally {
        //释放资源
        if (entry != null) {
          entry.exit();
        }
      }
    }).thenAccept(result -> System.out.println("result: " + result));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2、支持多种限流模式

    Sentinel 支持多种限流模式,可以根据实际需求选择不同的限流算法。

    • **直接模式:**直接对资源进行限制,超出阈值即触发限流。
    • **关联模式:**通过关联资源来进行限流,例如对某个 API 进行流量控制时,可以通过关联访问该 API 的数据库连接池来实现流量控制。通过 setRefResource 和 setStrategy 来关联数据库连接池资源,并设置限流策略为关联模式。
    • **链路模式:**通过对整个链路进行流量控制来保障系统的稳定性。

    注意:关联模式和链路模式时,需要在规则中设置相关的关联链接和链路信息。

    // 关联模式示例:
    @SentinelResource(value = "demoMethod", blockHandler = "handleBlock")
    public void demoMethod(@RequestParam("id") Long id) {
      System.out.println("request id: " + id);
    }
    
    @Bean
    public RequestOriginParser requestOriginParser() {
      return new DemoRequestOriginParser();
    }
    
    public static class DemoRequestOriginParser implements RequestOriginParser {
      @Override
      public String parseOrigin(HttpServletRequest request) {
        String origin = request.getParameter("origin");
        if (StringUtils.isEmpty(origin)) {
          return "unknown";
        }
        return origin;
      }
    }
    
    @Configuration
    public class SentinelConfig {
    
      @Autowired
      private RequestOriginParser requestOriginParser;
    
      @PostConstruct
      public void init() {
        FlowRuleManager.register2(Arrays.asList(
            new FlowRule("demoMethod").setCount(5)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setLimitApp("default")
                .as(FlowRule.class)
                .setStrategy(RuleConstant.STRATEGY_RELATE)
                .setRefResource("demoDatabase")));
      }
    
      @Bean
      public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
      }
    
      @Bean
      public SentinelServletRequestAspect sentinelServletRequestAspect() {
        return new SentinelServletRequestAspect();
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    3、支持多种规则匹配方式

    Sentinel 支持多种规则匹配方式,可以根据实际需求选择不同的规则匹配策略。

    • **精确匹配:**精确匹配是最常用的匹配方式,可以根据资源名称、限流参数等精确匹配规则。
    • 子串匹配:通常用于对资源名称进行模糊匹配,例如对某个 API 的所有请求进行限流。
    • **正则匹配:**可以根据正则表达式进行规则匹配,提供更高级别的灵活性。使用 setResourceRegex 方法设置了一个正则匹配规则
    //正则匹配规则的示例
    @SentinelResource(value = "demoMethod", blockHandler = "handleBlock")
    public void demoMethod(@RequestBody Map<String, Object> data) {
      System.out.println("request data: " + data);
    }
    
    @Configuration
    public class SentinelConfig {
    
      @PostConstruct
      public void init() {
        SystemRuleManager.loadRules(Collections.singletonList(
            new SystemRule()
                .setHighestSystemLoad(1.0)
                .setAvgLoad(0.8)
                .setQps(200))));
        ParamFlowRuleManager.loadRules(Collections.singletonList(
            new ParamFlowRule()
                .setParamIdx(0)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setCount(5)
                .setDurationInSec(1)
                .setParamFlowItemList(Collections.singletonList(
                    new ParamFlowItem().setObject("special_object")
                        .setCount(2)))));
        DegradeRuleManager.loadRules(Collections.singletonList(
            new DegradeRule("demoMethod")
                .setCount(100)
                .setTimeWindow(10)
                .setGrade(RuleConstant.DEGRADE_GRADE_RT)
                .setCount(20)
                .setMinRequestAmount(10))));
        FlowRuleManager.loadRules(Collections.singletonList(
            new FlowRule()
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setResourceRegex("/api/.*")
                .setCount(10)
                .setLimitApp("default")
                .as(FlowRule.class))));
      }
    
      @Bean
      public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    其他

    以后补充…

    总结

    ​ Sentinel是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。是一个非常不错的流量控制中间件。

  • 相关阅读:
    Spring中AOP使用场景
    文心一言 VS 讯飞星火 VS chatgpt (139)-- 算法导论11.4 3题
    【笔记:模拟MOS集成电路】偏置电路(基本原理+结构分析)
    Kafka 认证二:ScramLoginModule 认证及 Java 连接测试
    大学生阅读小说网页设计模板代码 小说书籍网页作业成品 学校书籍网页制作模板 学生简单书籍阅读网站设计成品
    STM32F407ZGT6|定时器中断
    Azure - 机器学习:创建机器学习所需资源,配置工作区
    cpp学习笔记:STL stack容器
    10【桥接设计模式】
    StringRedisTemplate
  • 原文地址:https://blog.csdn.net/qq_36010886/article/details/134426229