Sentinel核心代码解析

 

Sentinel介绍

Sentinel 是阿里开源的面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。
Github: https://github.com/alibaba/Sentinel
基于版本: 1.7.1
 

包结构

Sentinel-core

这个模块包含了Sentinel对于限流降级的核心逻辑代码, 接下来我们就对这个模块进行一个完整的解析, 看看它到底是怎么实现的限流, 以及它的设计思路
这里我们以最常用的流控规则(FlowRule), 组合SpringMvc接口限流为例, 看下它的逻辑是怎样的, 如下:
其中绿色的部分为关键路径和节点, 红色的部分就是限流的触发点与处理点

概念描述

Resource: 资源, 这是限流针对的目标, 在这里, 一个web接口就是一个资源, 比如: GET:/aaa , 此外, 资源还分为两大类: 入口资源 / 出口资源, 简单的区分方法: 如果应用是这个资源的提供方, 那么就是入口资源, 如果是调用方, 就是出口资源
Origin: 请求来源, 用来针对某一个来源进行限流配置, Sentinel默认获取请求header的S-user作为来源, 应用可以通过实现RequestOriginParser接口来自定义来源信息的获取方式
Context: 请求上下文, 每个request对应一个context对象, 由于context保存在contextHolder(ThreadLocal)中, 只要当前请求的线程没有主动销毁context, 就能随时通过contextHolder.get()获取到, 正常情况下context会在web拦截器开始的时候被创建, 在校验完限流规则后销毁. 需要注意的是, Context中有一个name属性是用来表示这个context的类型, 比如webmvc的context类型为: sentinel_spring_web_context, webservlet的context类型为: sentinel_web_servlet_context
ContextUil: 用来操作context的工具类, 比如创建context (entry), 销毁context (exit)
Node: 节点接口, 用来定义需要进行数据统计的节点
  StatisticNode: 是Node的基础实现, 基于滑动窗口实现的指标数据存储, 它应该属于抽象类, 只提供了数据统计的能力, 不代表具体统计维度
  ClusterNode: 继承StatisticNode, 它负责统计的是资源(resource)维度的访问数据
  DefaultNode: 继承StatisticNode, 它负责统计的是资源(resource) + context类型(contextName)维度的访问数据
  EntranceNode: 继承DefaultNode, 它负责统计的是context类型维度(contextName)的访问数据
Sph: 定义了获取资源访问权的接口, 代表性接口: Entry entry = sph.entry(String name);
  CtSph: Sph接口的默认实现, 负责校验流控规则, 当校验不通过时抛出BlockException异常
  SphU: 工具类, 封装了CtSph的调用, 未做特殊处理, 当校验不通过时抛出BlockException异常
  SphO: 工具类, 封装了CtSph的调用, 与SphU不同的是, 校验不通过时不会抛出异常, 而是return false;
Entry: 表示一个资源的访问权, 通过Sph接口获取, 因为被限流时是抛出BlockException异常的, 所以只要成功拿到了return的Entry, 就意味着获得了本地访问的权限, 故 Entry 在这里可以理解为访问权或门票. Entry中保存了当前的资源(resource), 当前的节点(node)
  CtEntry: Entry的子类, 它额外保存了当前的Context与ProcessorSlotChain
ProcessorSlotChain: 处理器插槽链, 可以理解为一个逻辑链, 需要注意的是, 一个资源(resource)的所有请求都是同享同一个chain对象的, 意味着 chain 和 resource 是一对一的欢喜
  ProcessorSlot: 处理器插槽, 定义了四个接口, 分别是: 执行插入(entry) / 插入完成(fireEntry) / 执行拔出(exit) / 拔出完成(fireExit) . 这个比较抽象, 属于设计模式层面的接口. 我们可以这样理解: entry是获取访问权的方法, fireEntry是获取访问权成功后的处理
  AbstractLinkedProcessorSlot: ProcessorSlot的抽象实现, 基于链表的形式, 将多个ProcessorSlot串联起来, 并在fireEntry后调用下一个ProcessorSlot的entry方法
  DefaultProcessorSlotChain: 继承AbstractLinkedProcessorSlot, 这个对象是由DefaultSlotChainBuilder创建, 是一个ProcessorSlot链, 第一个Slot是一个空逻辑的AbstractLinkedProcessorSlot, 往后的Slot顺序默认为: NodeSelectorSlot > ClusterBuilderSlot > LogSlot > StatisticSlot > AuthoritySlot > SystemSlot > FlowSlot > DegradeSlot
  NodeSelectorSlot: 这个Slot的逻辑就是给当前请求(context)绑定一个DefaultNode, 如果不存在则新建一个
  ClusterBuilderSlot: 这个Slot的逻辑就是给当前请求(context)绑定一个ClusterNode, 如果不存在则新建一个, 再提醒一遍, 一个资源(resource)的所有请求共享同一个ClusterNode
  LogSlot: 这个就是做一些日志记录..
  StatisticSlot: 这个Slot负责更新统计数据, 正常Slot在entry时是先处理自己的逻辑再触发fireEntry, 但是StatisticSlot则不是, 它是先触发fireEntry让后面的Slot先跑, 随后再根据后方的Slot执行结果进行处理, 比如后方处理异常了, 那么StatisticSlot就执行 block++, 如果后方处理通过了, 那么StatisticSlot就执行 pass++
  AuthoritySlot: Sentinel规则之授权规则, 它支持给资源配置来源白名单, 只有在白名单中的来源可以访问这个资源, 否则抛出异常AuthorityException
  SystemSlot: Sentinel规则之系统规则, 它基于系统入口维度(EntranceNode)的统计数据进行限流, 比如可以配置机器的webmvc入口请求qps不能超过100, 如果超过则抛出异常SystemBlockException
  FlowSlot: Sentinel规则之流控规则, 它基于资源维度(ClusterNode)或特定资源的来源维度(origin&resource的StatisticNode)进行限流, 如果超出阈值则抛出异常FlowException
  DegradeSlot: Sentinel规则之降级规则, 它基于资源维度(ClusterNode)进行限流, 如果超出阈值则抛出异常DegradeException
 

常见问题

流控规则的加载/更新过程?
Sentinel的每一类规则都有一个对应的Manager类进行管理, 比如Flow流控规则对应的就是 FlowRuleManager, 里面有一个静态的hashmap用来保存当前的规则集合(key是资源名称)
 
 
我们在FlowRuleManager中还发现了两个变量: FlowPropertyListener / SentinelProperty
SentinelProperty: 这是Sentinel中所有规则数据的更新入口, 每一类的规则都有一个对应的 sentinelProperty对象, 这些对象都以静态变量的方式绑定在XxxRuleManager类中
 
 
PropertyListerner: 从SentinelProperty接口中我们可以看到, 可以向SentinelProperty中注册监听器(listener), 并且SentinelProperty会在执行配置更新(updateValue)之后通知这些监听器
从FlowRuleManager类的代码中可以看到, LISTENER被注册到了currentProperty, 所以currentProperty配置更新时会触发LISTENER的逻辑, 那么这里的LISTENER逻辑是什么呢
 
 
其实FlowPropertyListener是FlowRuleManager的一个内部类, 基本的逻辑就是在数据变更后, 将数据赋值给flowRules这个hashmap
那SentinelProperty的updateValue又是谁触发的呢, 在FlowRuleManager中可以找到一个调用点
 
 
除了静态配置以外, 我们还可以将Sentinel配置封装到ApolloDatasource中, 以便实现配置动态变更, 我们可以看到在Apollo配置初始化以及变更事件中都调用了SentinelProperty的updateValue接口, 间接由LISTENER将配置保存到FlowRuleManager的flowRules集合中
 
 
 
 
 
 
 
posted @ 2022-02-15 23:43  EEEEET  阅读(399)  评论(0编辑  收藏  举报