0
点赞
收藏
分享

微信扫一扫

十分钟搞懂阿里Sentinel核心源码(上)


“​JavaEdge​”,


简介

Sentinel提供了丰富的限流、熔断功能。它支持控制台配置限流、熔断规则,支持集群限流,并可以将相应服务调用情况可视化。

本文主要分析Sentinel的限流功能源码。

Sentinel会进行流量统计,执行流量控制规则。而统计数据的展示和规则的设置在 sentinel-dashboard 项目中,这是一个 Spring MVC 应用,有后台管理界面,我们通过这个管理后台和各个应用进行交互。

当然,你不一定需要 dashboard,仅仅使用 sentinel-core,它会将统计信息写入到指定的日志文件中,通过该文件内容来了解每个接口的流量情况。这时只是使用到了 Sentinel 的流量监控功能。

dashboard 应用默认不持久化数据,它的所有数据都在内存,所以 dashboard 重启意味着所有数据都会丢失。你应按需要定制化 dashboard,如至少你应该要持久化规则设置,QPS 数据非常适合存放在时序数据库中,当然如果你的数据量不大,存 MySQL 也问题不大,定期清理一下过期数据即可,因为大部分人应该不会关心一个月以前的 QPS。

Sentinel 的核心将不同 Slot 按序串在一起(责任链模式),从而将不同功能(限流、降级、系统保护)组合在一起。核心结构:

十分钟搞懂阿里Sentinel核心源码(上)_spring

slot chain 其实可以分为两部分


  • 统计数据构建部分(statistic)
  • 判断部分(rule checking)

Sentinel 的数据统计

数据统计模块的内容,这样读者在后面看到相应的内容的时候心里有一些底。这节内容还是比较简单的,当然,如果你希望立马进入 Sentinel 的主流程,可以先跳过这一节。

Sentinel 的定位是流量控制,它有两个维度的控制,一个是控制并发线程数,另一个是控制 QPS,它们都是针对某个具体的接口来设置的,其实说资源比较准确,Sentinel 把控制的粒度定义为 Resource。

要做控制,首先就要先做统计,它要知道当前接口的 QPS 和并发是多少,进而判断一个新的请求能不能让它通过。

数据统计的代码在

StatisticNode

十分钟搞懂阿里Sentinel核心源码(上)_分布式_02

QPS 数据使用了滑动窗口:

十分钟搞懂阿里Sentinel核心源码(上)_编程语言_03

保存最近60秒的统计信息。windowLengthInMs 故意设置为1000毫秒,表示每秒每个桶,这样我们就可以获得每秒的准确统计信息。十分钟搞懂阿里Sentinel核心源码(上)_spring_04

线程数量的计数器,即统计并发量十分钟搞懂阿里Sentinel核心源码(上)_分布式_05

可知,Sentinel 统计了 秒 和 分 两个维度,现在看其实现类

ArrayMetric

Sentinel中的基本度量标准类,使用内部 BucketLeapArray。

十分钟搞懂阿里Sentinel核心源码(上)_java_06

属性

以分钟维度统计的使用来说,使用子类 BucketLeapArray 实现。

十分钟搞懂阿里Sentinel核心源码(上)_数据库_07

构造器

十分钟搞懂阿里Sentinel核心源码(上)_java_08

LeapArray

字段

十分钟搞懂阿里Sentinel核心源码(上)_java_09

条件(谓词)更新锁,仅在不使用当前桶时使用。十分钟搞懂阿里Sentinel核心源码(上)_java_10

内部核心数组 array,它的长度为 60,就是有 60 个窗口,每个窗口长度为 1 秒,一分钟走完一轮。然后下一轮开启“覆盖”操作。

十分钟搞懂阿里Sentinel核心源码(上)_数据库_11

每个窗口是一个 WindowWrap 类实例。

添加数据的时候,先判断当前走到哪个窗口了(当前时间(s) % 60 即可),然后需要判断这个窗口是否是过期数据,如果是过期数据(窗口代表的时间距离当前已经超过 1 分钟),需要先重置这个窗口实例的数据。

统计数据同理,如统计过去一分钟的 QPS 数据,就是将每个窗口的值相加,当中需要判断窗口数据是否是过期数据,即判断窗口的 WindowWrap 实例是否是一分钟内的数据。

核心逻辑都封装在了 currentWindow(long timeMillis) 和 values(long timeMillis)方法中。

添加数据的时候,我们要先获取操作的目标窗口,也就是

分维度数据统计

currentWindow

Sentinel 在这里处理初始化和过期重置的情况

十分钟搞懂阿里Sentinel核心源码(上)_spring_12

获取数据,使用的是

values

返回“有效”窗口中的数据

十分钟搞懂阿里Sentinel核心源码(上)_编程语言_13

isWindowDeprecated十分钟搞懂阿里Sentinel核心源码(上)_java_14

案例

十分钟搞懂阿里Sentinel核心源码(上)_分布式_15

红色部分的Context 代表一个调用链的入口,Context 实例设置在 ThreadLocal,所以它是跟着线程走的,如果要切换线程,需要手动切换。

ContextUtil#enter 有俩参数:

context name

调用链的入口,以区分不同调用链路,默认是 十分钟搞懂阿里Sentinel核心源码(上)_编程语言_16

origin

调用方标识,作用


  • 黑白名单的授权控制
  • 统计诸如从应用 application-a 发起的对当前应用 interfaceXxx() 接口的调用,目前这个数据会被统计,但是 dashboard 中并不展示

进入 BlockException 异常分支,代表该次请求被流量控制规则限制,一般会让代码走入到熔断降级逻辑。当然,BlockException 其实有好多个子类

十分钟搞懂阿里Sentinel核心源码(上)_数据库_17

亦可 catch 具体子类处理。

SphU#entry 方法的参数:


  • 第一个参数
    标识资源,通常就是我们的接口标识,对于数据统计、规则控制等,我们一般都是在这个粒度上进行的,根据这个字符串来唯一标识,它会被包装成 ResourceWrapper 实例。

  • 第二个参数
    标识资源的类型
    十分钟搞懂阿里Sentinel核心源码(上)_java_18



EntryType.IN

入口流量,比如我们的接口对外提供服务,那通常就是控制入口流量

EntryType.OUT

十分钟搞懂阿里Sentinel核心源码(上)_分布式_19

默认就是出口流量,它的业务需要调用订单服务,像这种情况,压力其实都在订单服务,那就指定它为出口流量。


流量类型在 SystemSlot 类中用以实现自适应限流,根据系统健康状态来判断是否要限流,如果是 OUT 类型,由于压力在外部系统中,所以就不需要执行该规则。

若在一个方法中写,要注意内层的 Entry 先 exit,才能做外层的 exit,否则会抛出异常。源码角度来看,是在 Context 实例中,保存了当前的 Entry 实例。

源码解析

ContextUtil

static 代码块

这里会添加一个默认的 EntranceNode 实例。

十分钟搞懂阿里Sentinel核心源码(上)_编程语言_20

enter

该行代码可不写,通常情况下,都不会显示设置 context。

ContextUtil.enter("user-center", "app-A");

如果不显式调用该方法,就会进入到默认 context。

十分钟搞懂阿里Sentinel核心源码(上)_spring_21

然后上面的这个方法会走进 ​​ContextUtil#trueEnter​​,添加名为 “user-center” 的 EntranceNode 节点:

十分钟搞懂阿里Sentinel核心源码(上)_java_22

若不显式调用 ​​ContextUtil#enter​​​,那 root 就只有一个默认节点 ​​sentinel_default_context​​。

context,线程执行的上下文,在 Sentinel 中对于一个新的 context name,Sentinel 会往树中添加一个 EntranceNode 实例。所以它的作用是为了区分调用链路,标识调用入口。在 sentinel-dashboard 中,我们可以很直观地看出调用链路:

十分钟搞懂阿里Sentinel核心源码(上)_数据库_23

SphU

entry

CtSph#entryWithPriority

十分钟搞懂阿里Sentinel核心源码(上)_spring_24

lookProcessChain(resourceWrapper)

链中每一个节点是一个 Slot 实例,这个链通过 BlockException 异常来告知调用入口最终的执行情况。

Sentinel 提供了 SPI 端点,让我们可以自己定制 Builder,如添加一个 Slot 进去。

由于 SlotChainBuilder 接口设计,我们只能全局所有的 resource 使用相同的责任链配置。

十分钟搞懂阿里Sentinel核心源码(上)_java_25

按照默认的 DefaultSlotChainBuilder 生成的责任链继续源码。

对相同的 resource,使用同一责任链实例,不同 resource,使用不同责任链实例。

resource 实例根据 resource name 来判断,和线程没有关系。

参考


  • https://github.com/alibaba/Sentinel/wiki/Sentinel-%E6%A0%B8%E5%BF%83%E7%B1%BB%E8%A7%A3%E6%9E%90
  • https://www.javadoop.com/post/sentinel

十分钟搞懂阿里Sentinel核心源码(上)_分布式_26

硬核干货:HTTP超时、重复请求必见坑点及解决方案

十分钟搞懂阿里Sentinel核心源码(上)_分布式_27

十分钟搞懂阿里Sentinel核心源码(上)_编程语言_28

喜欢文章



举报

相关推荐

0 条评论