Sentinel源码剖析之核心组件作用和介绍

简介: Sentinel 是分布式系统的防御系统。以流量为切入点,通过动态设置的流量控制、服务熔断降级、系统负载保护等多个维度保护服务的稳定性,通过服务降级增强服务被拒后用户的体验。

1、什么是Sentinel?

Sentinel 是分布式系统的防御系统。以流量为切入点,通过动态设置的流量控制、服务熔断降级、系统负载保护等多个维度保护服务的稳定性,通过服务降级增强服务被拒后用户的体验。

官网:https://github.com/alibaba/Sentinel/wiki/

2、Sentinel特性

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
  • 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

在这里插入图片描述

3、Sentinel 模块

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo/Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

在这里插入图片描述
在这里插入图片描述

4、Sentinel基本概念

Sentinel 实现限流、隔离、降级、熔断功能,本质要做的就是两件事:

  • 统计数据:统计某个资源的访问数据,例如:QPS、RT 等信息;
  • 规则判断:判断限流规则、隔离规则、降级规则、熔断规则是否满足。

资源

  • 资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。
  • 只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来表示资源。
  • 资源就是希望被 Sentinel 保护的业务,例如项目中定义的 Controller 方法,就是默认被 Sentinel 保护的资源

规则

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

5、Sentinel大致工作原理/工作流程

在这里插入图片描述

6、Sentinel核心组件

1、ProcessorSlotChain

Sentinel 的核心骨架是 ProcessorSlotChain,这个类基于责任链模式来设计,将不同的功能(限流、降级、系统保护等)封装为一个一个的 Slot,请求进入后逐个执行即可。系统会为每个资源创建一套 SlotChain。SlotChain 其实可以分为两部分:统计数据构建部分(statistic)和判断部分(rule checking)

2、责任链中的 SlotChain 分为两大类

统计数据构建部分(statistic)

  • NodeSelectorSlot:负责构建簇点链路中的节点(DefaultNode),将这些节点形成链路树
  • ClusterBuilderSlot:负责构建某个资源的 ClusterNode,ClusterNode 可以保存资源的运行信息以及来源信息(origin 名称),例如响应时间、QPS、block 数量、线程数、异常数等
  • StatisticSlot:负责统计实时调用数据,包括运行信息、来源信息等

规则判断部分(rule checking)

  • AuthoritySlot:负责授权规则(来源控制)(黑白名单)
  • LogSlot:负责日志打印
  • SystemSlot:负责系统保护规则(系统qps,thread等等防护)
  • ParamFlowSlot:负责热点参数限流规则
  • FlowSlot:负责普通限流规则
  • GatewayFlowSlot:负责网关限流规则
  • DegradeSlot:负责降级规则

3、slotChain调用顺序

在这里插入图片描述

4、Context

Context 是对资源操作的上下文,每个资源操作必须属于一个 Context。如果代码中没有指定 Context,则会创建一个 name 为 sentinel_default_context 的默认 Context。一个 Context 生命周期中可以包含多个资源操作。Context 生命周期中最后一个资源在 exit() 时会清理该 Context,也就意味着这个 Context 生命周期结束了

说白了context指的是 调用方访问受保护的资源时,调用链路的上下文

  • Context 代表调用链路上下文,贯穿一次调用链路中的所有资源(Entry)。Context 维持着入口节点(entranceNode)、本次调用链路的 curNode、调用来源(origin)等信息;
  • Context 维持的方式:通过 ThreadLocal 传递,只有在入口 enter 的时候生效。由于 Context 是通过 ThreadLocal 传递的,因此对于异步调用链路,线程切换的时候会丢掉 Context,因此需要手动通过 ContextUtil.runOnContext(context, f) 来变换 context;

后续的 Slot 都可以通过 Context 拿到 DefaultNode 或者 ClusterNode,从而获取统计数据,完成规则判断;

  • Context 初始化的过程中会创建 EntranceNode ,entranceNode Name往往指的是调用方,资源,default。entranceNode 作为流量入口,作为一个统计维度
public void contextDemo() {
    // 创建一个来自于 appA 访问的 Context,”entranceOne“ 为 Context 的名称,”appA“ 为来源名称
    ContextUtil.enter("entranceOne", "appA");
    
    // Entry 就是一个资源操作对象
    Entry resource1 = null;
    Entry resource2 = null;
    
    try {
        // 获取资源 resource1 的 entry
        resource1 = SphU.entry("resource1");
        // 代码至此,说明当前对资源 resource1 的请求通过了流控
        // 对资源 resource1 的相关业务处理。。。

        // 获取资源 resource2 的 entry
        resource2 = SphU.entry("resource2");
        // 代码至此,说明当前对资源 resource2 的请求通过了流控
        // 对资源 resource2 的相关业务处理。。。
    } catch (BlockException e) {
        // 代码至此,说明请求被限流,这里执行降级处理
    } finally {
        if (resource1 != null) {
            resource1.exit();
        }
        if (resource2 != null) {
            resource2.exit();
        }
    }
    // 释放Context
    ContextUtil.exit();

    // --------------------------------------------------------

    // 创建另一个来自于 appA 访问的 Context,
    // entranceTwo 为 Context 的 name
    ContextUtil.enter("entranceTwo", "appA");
    
    // Entry 就是一个资源操作对象
    Entry resource3 = null;
    
    try {
        // 获取资源 resource2 的 entry
        resource2 = SphU.entry("resource2");
        // 代码至此,说明当前对资源 resource2 的请求通过了流控
        // 对资源 resource2 的相关业务处理。。。


        // 获取资源 resource3 的 entry
        resource3 = SphU.entry("resource3");
        // 代码至此,说明当前对资源 resource3 的请求通过了流控
        // 对资源 resource3 的相关业务处理。。。

    } catch (BlockException e) {
        // 代码至此,说明请求被限流,这里执行降级处理
    } finally {
        if (resource2 != null) {
            resource2.exit();
        }
        if (resource3 != null) {
            resource3.exit();
        }
    }
    // 释放Context
    ContextUtil.exit();
}

5、Entry

entry为令牌,当访问受限的资源被允许时,会获取到entry令牌

  • 每一次资源调用都会创建一个 Entry。Entry 包含了资源名、curNode(当前统计节点)、originNode(来源统计节点)等信息。
  • CtEntry 为普通的 Entry,在调用 SphU.entry(xxx) 的时候创建。特性:Linked entry within current context(内部维护着 parent 和 child)
  • 需要注意的一点:CtEntry 构造函数中会做调用链的变换,即将当前 Entry 接到传入 Context 的调用链路上(setUpEntryFor)。
  • 资源调用结束时需要 entry.exit()。exit 操作会过一遍 slot chain exit,恢复调用栈,exit 会清空 entry 中的 context 防止重复调用

6、Node

簇点链路:就是项目内的调用链路,链路中被监控的每个接口就是一个资源。默认情况下 sentinel 会监控 SpringMVC 的每一个端点(Endpoint),因为 SpringMVC 的每一个端点就是调用链路中的一个资源。流控、熔断等都是针对簇点链路中的资源来设置的。

注意哦!

簇点链路: 通俗的讲是上下文Context中的逻辑链路,context内部包含着从entranceNode出发的到受保护资源的至少一条链路,不同使用方式,链路树可能会不一样哦,entranceNode往往作为调用方!

Sentinel 里面的各种种类的统计节点,即簇点链路是由一个个 Node 组成的:

在这里插入图片描述

所有的节点都可以记录对资源的访问统计数据,因为都是 StatisticNode 的子类,StatisticNode 是最为基础的统计节点,包含秒级和分钟级两个滑动窗口结构

/**
 * 定义了一个使用数组保存数据的计量器(以"秒"为单位)
 * SAMPLE_COUNT:样本窗口数量,默认值为 2
 * INTERVAL:时间窗长度,默认值为 1000 毫秒
 */
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,
    IntervalProperty.INTERVAL);

/**
 * 定义了一个使用数组保存数据的计量器(以"分"为单位)
 */
private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);

节点 Node 按照作用分为以下几类:

  • DefaultNode:链路节点,用于统计调用链路上某个资源的数据,维持树状结构,即代表链路树中的每一个资源。一个资源出现在不同链路中时,会创建不同的 DefaultNode 节点
  • ClusterNode:簇点,代表资源,一个资源不管出现在多少链路中,只会有一个 ClusterNode,用于记录当前资源被访问的所有统计数据之和。即用于统计每个资源全局的数据(不区分调用链路),以及存放该资源的按来源区分的调用数据(类型为 StatisticNode)。特别是 Constants.ENTRY_NODE 节点用于统计全局的入口资源数据
  • EntranceNode入口节点,特殊的链路节点(DefaultNode),对应某个 Context 入口的所有调用数据。Constants.ROOT 节点也是入口节点。

例如:

现在两个业务接口:

业务 1:Controller 中的资源 /order/query 访问了 Service 中的资源 /cat
业务 2:Controller 中的资源 /order/save 访问了 Service 中的资源 /cat

创建的链路图如下:

在这里插入图片描述
Node 节点总结:

  • Node:用于完成数据统计的接口;
  • StatisticNode:统计节点,是 Node 接口的实现类,用于完成数据统计;
  • EntranceNode:入口节点,一个 Context 会有一个入口节点,用于统计当前 Context 的总体流量数据;
  • DefaultNode:默认节点,用于统计一个资源在当前 Context 中的流量数据;
  • ClusterNode:集群节点,用于统计一个资源在所有 Context 中的总体流量数据;

7、Slot插槽

1、NodeSelectorSlot 用户调用链路构建

负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级。

ContextUtil.enter("entrance1", "appA");
 Entry nodeA = SphU.entry("nodeA");
 if (nodeA != null) {
    nodeA.exit();
 }
 ContextUtil.exit();

上述代码通过 ContextUtil.enter() 创建了一个名为 entrance1 的上下文,同时指定调用发起者为 appA;接着通过 SphU.entry()请求一个 token,如果该方法顺利执行没有抛 BlockException,表明 token 请求成功。

根据调用者的不同(entranceNode)(context)不同,同一资源存在者,多种调用链路

ContextUtil.enter("entrance1", "appA");
  Entry nodeA = SphU.entry("nodeA");
  if (nodeA != null) {
    nodeA.exit();
  }
  ContextUtil.exit();

ContextUtil.enter("entrance2", "appA");
  nodeA = SphU.entry("nodeA");
  if (nodeA != null) {
    nodeA.exit();
  }
  ContextUtil.exit();

在内存中生成以下结构

                   machine-root
                   /         \
                  /           \
          EntranceNode1   EntranceNode2
                /               \
               /                 \
       DefaultNode(nodeA)   DefaultNode(nodeA)

2、ClusterBuilderSlot ,统计簇点构建(资源的所有链路数据统计)

用于存储资源的统计信息以及调用者信息,例如该资源的 RT,QPS,thread count,Block count,Exception count 等等,这些信息将用作为多维度限流,降级的依据。简单来说,就是用于构建 ClusterNode。

3、StatisticSlot 数据统计

StatisticSlot 是 Sentinel 最为重要的类之一,用于根据规则判断结果进行相应的统计操作。用于记录、统计不同纬度的 runtime 指标监控信息

  • 执行 entry() 方法的时候:依次执行后面的判断 slot。每个 slot 触发流控的话会抛出异常(BlockException 的子类)。若有 BlockException 抛出,则记录 block 数据;若无异常抛出则算作可通过(pass),记录 pass 数据。
  • 执行 exit() 方法的时候:若无 error(无论是业务异常还是流控异常),记录 complete(success)以及 RT,线程数-1。
  • 记录数据的维度:线程数+1、记录当前 DefaultNode 数据、记录对应的 originNode 数据(若存在 origin)、累计 IN 统计数据(若流量类型为 IN)。

StatisticSlot 是 Sentinel 的核心功能插槽之一,用于统计实时的调用数据。

  • clusterNode:资源唯一标识的 ClusterNode 的实时统计
  • origin:根据来自不同调用者的统计信息
  • defaultNode: 根据入口上下文区分的资源 ID 的 runtime 统计
  • 入口流量的统计

4、ParamFlowSlot

热点参数限流,对应热点流控。
热点参数限流是分别统计参数值相同的请求,判断是否超过 QPS 阈值。

5、FlowSlot

用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制。对应流控规则。

这个 slot 主要根据预设的资源的统计信息,按照固定的次序,依次生效。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:

三种流控模式:

  • 直接模式:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
  • 关联模式:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
  • 链路模式:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

三种流控模式,从底层数据统计角度来看,分为两类

  • 对进入资源的所有请求(ClusterNode)做限流统计:直接模式、关联模式
  • 对进入资源的部分链路(DefaultNode)做限流统计:链路模式

三种流控效果:

  • 快速失败:达到阈值后,新的请求会被立即拒绝并抛出 FlowException 异常。是默认的处理方式,基于滑动时间窗口算法。
  • warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。基于滑动时间窗口算法,只不过阈值是动态的
  • 排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长,基于漏桶算法。

滑动时间窗的功能分两部分:

6、GatewayFlowSlot 同上

7、AuthoritySlot

来源访问控制

根据配置的黑白名单和调用来源信息,来做黑白名单控制。对应授权规则 。

白名单:来源(origin)在白名单内的调用者允许访问
黑名单:俩与(origin)在黑名单内的调用者不允许访问

8、DegradeSlot

熔断降级

通过统计信息及预设的规则,来做熔断,对应降级规则。

熔断降级是解决雪崩问题的重要手段,其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求,而当服务恢复时,断路器会放行访问该服务的请求

在这里插入图片描述

器熔断策略有三种:慢调用、异常比例、异常数

  • 慢调用:业务的响应时长(RT)大于指定时长的请求被认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。例如下图配置,RT 超过 500ms 的调用是慢调用,统计最近 10000ms 内的请求,如果请求量超过 10 次,并且慢调用比例不低于 0.5,则触发熔断,熔断时长为 5s,然后进入 half-open 状态,放行一次请求做测试
  • 异常比例或异常数:统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。例如下图配置,统计最近 1000ms 内的请求,如果请求超过 10 次,并且异常比例不低于 0.4,则触发熔断,熔断时长为 5s,然后进入 half-open 状态,放行一次请求做测试

9、SystemSlot

系统保护

通过系统的状态,例如 load1 等,来控制总的入口流量。对应系统规则。

这个 slot 会根据对于当前系统的整体情况,对入口资源的调用进行动态调配。其原理是让入口的流量和当前系统的预计容量达到一个动态平衡。

注意系统规则只对入口流量起作用(调用类型为 EntryType.IN),对出口流量无效。可通过 SphU.entry(res, entryType) 指定调用类型,如果不指定,默认是 EntryType.OUT

相关文章
|
存储 NoSQL 调度
【Redis源码】集群之哨兵sentinel故障转移 (十二)
【Redis源码】集群之哨兵sentinel故障转移 (十二)
161 0
|
自然语言处理 监控 Cloud Native
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
1032 0
精华推荐 |【深入浅出Sentinel原理及实战】「原理探索专题」完整剖析Alibaba微服务架构体系之轻量级高可用流量控制组件Sentinel(1)
|
4月前
|
监控 Java 应用服务中间件
SpringCloud面试之流量控制组件Sentinel详解
SpringCloud面试之流量控制组件Sentinel详解
229 0
|
6月前
|
监控 Java Nacos
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程(上)
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程
250 0
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程(上)
|
存储 NoSQL Redis
【Redis源码】集群之哨兵sentinel初识(十一)
【Redis源码】集群之哨兵sentinel初识(十一)
90 0
|
Sentinel 微服务
微服务组件(高并发带来的问题 服务器雪崩效应 Sentinel入门)2
微服务组件(高并发带来的问题 服务器雪崩效应 Sentinel入门)2
136 0
|
Java 测试技术 Nacos
Sentinel源码改造,实现Nacos双向通信!
Sentinel源码改造,实现Nacos双向通信!
221 0
Sentinel源码改造,实现Nacos双向通信!
|
6月前
|
监控 Dubbo Linux
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程(下)
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程(下)
170 0
|
6月前
|
算法 API 网络架构
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程(中)
【分布式流控组件 Sentinel 快速入门】——图文详解操作流程(中)
168 0
|
存储 SpringCloudAlibaba 监控
系统高可用番外篇:浅析sentinel源码
Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从**限流、流量整形、熔断降级、系统负载保护、热点防护**等多个维度来帮助开发者保障微服务的稳定性。
153 0
系统高可用番外篇:浅析sentinel源码