Sentinel 实现原理——概述

引言

在前面的文章中,我已经介绍了 Sentinel 中的概念,以及所提供的各类功能如何使用。从本篇文章开始,我们将深入到源码中,自顶向下地介绍 Sentinel 整体的实现原理以及各个核心模块的实现原理。本文作为这一部分介绍的开篇,我会先介绍一下 Sentinel 的整体设计思想,以及下层包含的各个模块,后续的文章中会详细地介绍各个核心模块的实现原理。更多相关文章和其他文章均收录于贝贝猫的文章目录

整体设计

接下来,我们会分别从数据和处理过程这两个角度介绍 Sentinel 的设计原理,首先,我们先介绍一下 Sentinel 中内部是如何组织数据的,通过对数据结构的观察,大家就能清楚地认识到 Sentinel 是如何实现链路流控、关联流控、直接流控、访问源流控这些功能的了。

数据

这里我们以一次完整的使用流程出发,展开对 Sentinel 数据部分的介绍。在下面的例子中,我们先定义代码执行的 Context,然后调用 SphU.entry 入口函数,在执行完正常业务逻辑后,我们从 entry 中退出,如果期间出现了异常,则通过 Tracer.trace 统计异常。

// 处理 HTTP 请求
public String handleHttpRequest(String originIp) {
    ContextUtil.enter("HTTPResource1Entrance", originIp);
    return doSomeThing();
}

// 处理 RPC
public String handleRPC(String consumerName) {
    ContextUtil.enter("RPCResource1Entrance", consumerName);
    return doSomeThing();
}

public String doSomeThing(String param, String origin) {
    Entry entry1 = null;
    try {
        entry1 = SphU.entry("Resource1");
        // do some thing
        doOtherThing();
        return "Result";
    } catch (BlockException e) {
        return "System busy";
    } catch (Exception ex) {
        Tracer.trace(ex);
    } finally {
        if (entry1 != null) {
            entry1.exit();
        }
    }
}

public void doOtherThing(String param) {
    Entry entry2 = null;
    try {
        entry2 = SphU.entry("Resource2");
        // do other thing
    } catch (BlockException e) {
        throw e;
    } catch (Exception ex) {
        Tracer.trace(ex);
    } finally {
        if (entry2 != null) {
            entry2.exit();
        }
    }
}

在上面的例子中,主要涉及到了 Sentinel 中几个核心的概念,Resource, Entry,Context。

  • Resource: 资源是 Sentinel 对所保护的内容的抽象,任何您想保护的代码、函数等都可以通过 SphU.entry 接口将其定义为一个资源,SphU.entry 接口的第一个参数描述了该资源的名称。Sentinel 会创建一些和该资源相关的统计节点,它们肩负着保存各类统计量的责任(QPS, Exception数量等)。这里大家可能会有疑问:为什么一个资源需要多个统计节点呢?因为 Sentinel 所支持的限流维度很多,比如针对访问源的针对链路的等等,这每一个维度都需要一个独立的统计节点,就比如上例中,我将 doSomeThing 这个函数定义为一个名为 Resource1 的资源,那么当执行了 doSomeThing 后,Sentinel 内部就会有和 Resource1 相关的各类统计节点,这也就意味着后续只要我们添加了针对 Resource1 资源的流控规则,就能限制 doSomeThing 这个函数的调用。
  • Node: Sentinel 里面的各种种类的统计节点:
    • StatisticNode:最为基础的统计节点,是后续三种节点的父类,包含秒级和分钟级两个滑动窗口结构。
    • DefaultNode:链路节点,用于统计调用链路上某个资源的数据,维持树状结构。
    • ClusterNode:簇点,用于统计每个资源全局的数据(不区分调用链路),以及存放该资源的按来源区分的调用数据
    • EntranceNode:入口节点,特殊的链路节点,对应某个 Context 入口的所有调用数据。
  • Entry: 又称为调用点,这里面保存着和某一特定资源的某一次调用相关的信息,比如这次调用的起始执行时间,结束时间,是否抛出异常,调用链路中的父子关系,该调用点对应资源的处理链(ProcessorSlotChain),还保存了该调用点所对应的资源统计节点
  • Context: Context 代表调用链路上下文,贯穿一次调用链路中的所有 Entry,每次调用链路开始时,都会创建一个新的 Context 实例,Context 名称即为调用链路入口名称,Context 是保存调用链路元数据的容器,这里所说的元数据主要包括:
    • 当前调用树的根节点,通过这个根节点,我们可以用来区分不同的调用链路,比如上述的 HTTPResource1EntranceRPCResource1Entrance 就是两个不同的根节点,ContextUtil.enter 接口的第一个参数描述了该调用链路的根节点的名称,如果没有显式地调用 ContextUtil.enter 接口,那么 Sentinel 会以 sentinel_default_context 作为默认根节点,Sentinel 会保证每个名称的根节点实例是唯一的
    • 调用源 origin,通过ContextUtil.enter 接口的第二个参数指定,一般会将 Consumer name 或者 consumer IP 地址设定为调用源
    • 当前的执行点 Entry,因为 Context 是保存在 ThreadLocal 中的,所以当前线程每到一个执行点,就会将其记录在 Context 中

在下面这张图中,大家可以看到上例中 Context, Entry, Resource 数据之间是如何组织的,其中 Context 主要用来保存当前调用树的 Origin,调用树根节点,以及当前执行到的调用点。而整个调用链路是通过 Entry 的父子指针来描述的,同时 Entry 中还会保存相同根节点下访问当前资源的统计节点(我称之为 DefaultNode),Sentinel 的链路模式流控功能就是通过这个统计节点实现的。为了统计某一资源的所有访问量,Sentinel 中还对上述统计量进行了聚合,聚合后的统计节点会包含某一资源在不同入口下的所有访问量(我称之为 ClusterNode),Sentinel 直接模式和关联模式流控功能就是通过这个统计节点实现的。最后,Sentinel 为了能够根据访问源进行流控还会针对每一个资源的每一类访问源分别进行流量统计(我称之为 OriginNode)。
relation-context-node-entry
上面的图中,旨在介绍 Context,Entry,和 Resource 各个统计节点之间的关系,而且各个统计节点(EntranceNode, DefaultNode, ClusterNode, OriginNode)之间的的关系并没有完整的展示出来。下面我将统计节点之间的关系单独抽了出来,通过下图大家应该能够更加清晰地理解各个统计节点之间的关系。
node-relation
那么,上述的这些数据是通过什么方式组织在一起的呢?接下来我们就来介绍一下这个部分。

数据的处理

首先,在调用 ContextUtil.enter(xxx) 时,会创建对应的 EntranceNode 节点并保存在 ThreadLocal 的 Context 中。如果没有显式地调用 ContextUtil.enter(xxx) 的话,它会在 SphU.entry 接口中被自动调用,生成一个默认的 Context。

当执行到 SphU.entry 接口时,就到了 Sentinel 的核心骨架——处理链(ProcessorSlotChain)了,Sentinel 将不同的 Slot 按照顺序串在一起(责任链模式),从而将不同的功能(限流、降级、系统保护)组合在一起。Slot Chain 其实可以分为两部分:统计数据构建部分(statistic)和判断部分(rule checking)。其核心结构如下:
structure

上图仅作为设计思想的展示,图中 Slot 的顺序已和最新版 Sentinel Slot Chain 顺序不一致,目前每一个 Resource 都会有一个对应的 ProcessorSlotChain,各个 Resource 的 ProcessorSlotChain 是隔离的。

上图中很明确的展示了各个 Slot 如何组织数据:

  • NodeSelectorSlot:根据 context 创建 DefaultNode,然后维护整个调用树的父子关系,然后将相关数据保存在 Context 中。
  • ClusterBuilderSlot:首先根据 resourceName 创建 ClusterNode,并将其引用保存在 defaultNode中,然后再根据 origin 创建来源节点(类型为 StatisticNode),并将源节点保存在当前的调用点 Entry 中。
  • StatisticSlot:先调用后续 Slot 的检查过程,如果检查通过增加各个维度的计数器
    • entry 过程:依次执行后面的判断 slot。每个 slot 触发流控的话会抛出异常(BlockException 的子类)。若有 BlockException 抛出,则记录 block 数据;若无异常抛出则算作可通过(pass),记录 pass 数据。
    • exit 过程:若无 error(无论是业务异常还是流控异常),记录 complete(success)以及 RT,线程数-1。
  • 后续各个检查 Slot: 根据定义的规则,结合统计节点的数据判断是否通过,如果通过则执行下一个 Slot 的处理过程,否则抛出相应的异常

最后,总结一下各个统计节点的数据规模:

  • ClusterNode 的规模是 resource count
  • DefaultNode 的规模是 resource count * context count,存在每个 NodeSelectorSlot 的 map 里面
  • EntranceNode 的规模是 context count,存在 ContextUtil 类的 contextNameNodeMap 里面
  • OriginNode(类型为 StatisticNode)的维度是 resource count * origin count,存在每个 ClusterNode 的 originCountMap 里面

代码结构

Sentinel 的整个项目结构都很清晰,下面我简单地介绍一下项目中各个模块的职责,在后续的文章中,我们会对主要的模块进行源码实现介绍。
project-module

  • sentinel-core: 核心模块,限流、降级、系统保护等都在这里实现
  • sentinel-dashboard: 控制台模块,可以对连接上的sentinel客户端实现可视化的管理
  • sentinel-transport: 传输模块,提供了基本的监控服务端和客户端的API接口,以及一些基于传输方式的实现(Netty,HTTP)
  • sentinel-extension: 扩展模块,主要对 DataSource、注解支持、热点参数流控进行了部分扩展实现
  • sentinel-adapter: 适配器模块,主要实现了对一些常见框架的适配
  • sentinel-demo: 样例模块,介绍了如何使用sentinel进行限流、降级等
  • sentinel-benchmark: 基准测试模块,对核心代码的精确性提供基准测试
  • sentinel-cluster: 提供集群流控的支持,主要包括令牌服务器的 Server 端和 Client 端实现
  • sentinel-logging: 提供 Logger 实现的拓展能力

参考内容

[1] Sentinel GitHub 仓库
[2] Sentinel 官方 Wiki
[3] Sentinel 1.6.0 网关流控新特性介绍
[4] Sentinel 微服务流控降级实践
[5] Sentinel 1.7.0 新特性展望
[6] Sentinel 为 Dubbo 服务保驾护航
[7] 在生产环境中使用 Sentinel
[8] Sentinel 与 Hystrix 的对比
[9] 大流量下的服务质量治理 Dubbo Sentinel初涉
[10] Alibaba Sentinel RESTful 接口流控处理优化
[11] 阿里 Sentinel 源码解析
[12] Sentinel 教程 by 逅弈
[13] Sentinel 专题文章 by 一滴水的坚持

stun

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Sentinel 是一款由阿里巴巴开源的分布式系统流量防卫兵。它的主要目标是通过实时的流量控制、熔断降级和系统负载保护等手段,来保证系统的稳定性和可靠性。 Sentinel实现原理主要包括以下几个方面: 1. 熔断降级:Sentinel 通过定义规则,监控系统中的资源(如接口、方法),当资源达到设定的阈值时,可以进行熔断,即临时关闭该资源的访问,避免故障扩大化。熔断后,Sentinel 会定时尝试恢复资源的访问,并根据具体情况逐渐放量。 2. 流量控制:Sentinel 可以对系统中的资源进行实时流量控制。通过定义规则,可以限制资源的最大访问量或并发数,防止系统被过高的请求压垮。 3. 系统负载保护:Sentinel 可以根据系统当前的负载情况,动态地调整流量控制策略。当系统负载过高时,可以通过限制资源的最大访问量来保护系统不被过载。 4. 实时监控和统计:Sentinel 提供了实时监控和统计功能,可以及时地了解系统中各个资源的运行情况和负载情况。通过监控数据,可以进行性能优化和故障排查。 相关问题: 1. 如何在项目中集成 Sentinel? 2. Sentinel 如何实现对资源的实时监控和统计? 3. Sentinel 的熔断降级策略是如何工作的? 4. 如何定义 Sentinel 的流量控制规则? 5. Sentinel 如何保证对系统的负载保护不会影响正常的业务流程?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝克街的流浪猫

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值