文章目录
- 前言
- 一、客户端与Spring Boot整合
- 二、SphU.entry
- 2.1、构建责任链
- 2.2、调用责任链
- 2.2.1、NodeSelectorSlot
- 2.2.2、ClusterBuilderSlot
- 2.2.3、LogSlot
- 2.2.4、StatisticSlot
- 2.2.5、AuthoritySlot
- 2.2.6、SystemSlot
- 2.2.7、FlowSlot
- 2.2.7.1、selectNodeByRequesterAndStrategy
- 2.2.7.2、canPass
 
- 2.2.8、DegradeSlot
 
 
- 总结
前言
  Sentinel作为Spring cloud alibaba中流控的组件,在微服务架构中也有广泛的应用。其核心源码主要体现在客户端。客户端在启动时,和Nacos类似,也会将自己的信息注册到服务端。而服务端的页面上配置各种规则时,实际上也是将信息发送到了客户端。
 
   我们最常使用的@SentinelResource注解:
 
   底层也是基于AOP + 责任链模式实现的。**Sentinel的难点不在于处理流程,而在于限流的算法。**本篇仅介绍Sentinel责任链的核心流程。
一、客户端与Spring Boot整合
在Spring Boot项目中,如果需要引入Sentinel,通常需要在pom文件中加入:
        <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
  该组件也是利用了Spring Boot的自动配置:
 
   其中的SentinelAutoConfiguration是核心:
 
   在SentinelAutoConfiguration中,会注册SentinelResourceAspect Bean:
 
   SentinelResourceAspect实际上是一个切面,匹配了所有加入了@SentinelResource注解的方法:
 
   invokeResourceWithSentinel是一个环绕通知。。在执行目标方法之前,首先会得到目标方法对象,以及处理注解中的一些信息,然后调用SphU.entry方法。该方法是Sentinel流程的核心。
 
   点击进去,会调用到entry方法:
 
   Env在实例化之前,会触发static中的逻辑:
 
   在doInit方法中,又会通过SPI机制,加载InitFunc中的类:
 
 
   其中的HeartbeatSenderInitFunc是定期向服务端发送心跳的:
 
 
   CommandCenterInitFunc,是将客户端各种接收规则的接口信息,暴露给服务端:
 
   上面的逻辑,包括后续构建,执行责任链,是在切面中,并非是在应用启动时执行的,而是在执行加入了@SentinelResource注解的方法时才会去执行!
二、SphU.entry
  SphU.entry方法内部主要做了两件事:
- 构建责任链。
- 按照顺序依次调用责任链。

2.1、构建责任链
  在进入lookProcessChain方法后,首先通过双检锁模式,判断当前加入了@SentinelResource注解的方法,是否已经为其构建过责任链,如果没有,才会执行newSlotChain方法。也就是说,是每一个加入了注解的方法,都有一个对应的责任链,并且只在应用启动后该方法第一次被调用时初始化。
 
   最终调用的是DefaultSlotChainBuilder的build方法:
 
   在该方法中,主要做了两件事:
 
  通过SPI机制,加载ProcessorSlots文件中的类(责任链中的具体组成类)。

   真正地去构建责任链:在执行ProcessorSlotChain chain = new DefaultProcessorSlotChain();这一段代码时,实际上是构造了:
 
   构造出的是下图的数据结构,end是指向first的引用:
 
   在构造完成后,就会利用chain.addLast((AbstractLinkedProcessorSlot<?>) slot);方法,向上图的数据结构中插入具体的责任链类了:
 
   首先将end的next指针指向NodeSelectorSlot,因为end是指向指向first的引用,实际上first的next指针也指向了NodeSelectorSlot:
 
   然后将end指向NodeSelectorSlot:
 
   以此类推,最终构建出的责任链是:
 
 图片来源:图灵学院
2.2、调用责任链
  这里我们重点关注FlowSlot和DegradeSlot,它们是sentinel核心功能-限流熔断降级的体现。
2.2.1、NodeSelectorSlot
  首先调用的是NodeSelectorSlot,它的作用是构建资源调用的统计节点,用于记录调用链路信息,并且将资源关联到相应的 DefaultNode。
 
   fireEntry就是在满足条件的情况下,继续调用后续的责任链。
2.2.2、ClusterBuilderSlot
  ClusterBuilderSlot和NodeSelectorSlot是类似的,它的作用是构建统计节点的聚合关系。但是NodeSelectorSlot 是按调用链统计,ClusterBuilderSlot 是按资源维度统计。
 
2.2.3、LogSlot
  LogSlot的作用,是在后续的责任链调用过程中出现异常时,进行日志的记录,体现在它的try…catch中:
 
2.2.4、StatisticSlot
  StatisticSlot是先将请求放行到后续的责任链,在后续的责任链调用完成后,再去进行统计资源的调用情况的操作。例如记录 QPS(每秒请求数)、RT(响应时间)、线程数、异常数等。
 
   包括抛出了各种异常之后的记录,这些记录都是执行降级、限流等控制的基础数据来源。
 
2.2.5、AuthoritySlot
  AuthoritySlot是进行授权规则的检查,例如黑白名单:
 
  简单回顾一下黑白名单的使用,首先需要在sentinel控制台的授权规则选项卡进行配置:
 
   这里的资源名,是http请求的路径,而流控应用,可以是特定的ip,也可以是请求路径:
@Component
public class IPLimiter implements RequestOriginParser {/***	获取当前服务实例的ip*/@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {return httpServletRequest.getRemoteAddr();}
}
  在checkBlackWhiteAuthority方法中,首先会获取所有设置的规则,然后根据当前的资源名,获取该资源对应的所有规则
 
   然后进行检查:
 
   这里的黑白名单体现在RuleConstant这个常量类中:
 
   判断逻辑有点绕:
- 如果规则中的IP或路径,和请求中的匹配,contain会为true,反之为false。
- 在黑名单的判定中,如果contain为true,则返回false,代表请求不通过。因为黑名单就是要对能和规则匹配上的请求进行拦截。
- 在白名单的判断中,如果contain为false,则返回false,代表请求不通过。因为contain为false,代表请求和规则匹配不上,也就是不在白名单中。

2.2.6、SystemSlot
  SystemSlot是对系统规则进行控制,包括系统整体的 QPS,平均响应时间(RT),当前系统的并发线程数等。
 
 
2.2.7、FlowSlot
  FlowSlot是 Sentinel 的核心功能之一,用于流量控制(限流)规则判断。
 
   同样会获取到控制台设置的所有规则,然后逐个进行匹配:
 
   最终调用到的是passLocalCheck,其中也有两个关键方法:
 
2.2.7.1、selectNodeByRequesterAndStrategy
  selectNodeByRequesterAndStrategy用于在执行限流时 选择哪个节点(Node)来做统计和判断。不同的来源(origin)和限流策略(strategy)决定了限流数据统计的维度。首先会获取到流控模式,也就是控制台设置的:
 
- 匹配指定 origin 的限流 - 如果是直接模式,就利用context.getOriginNode();调用方自己的统计节点限流。
- 如果是其他策略,利用selectReferenceNode再次匹配:
 
- 如果是

- 如果来源是default:- 直接限流时,使用当前资源的全局统计节点
- 非直接限流,使用关联资源的统计节点。
 
- 如果来源是other:- 直接限流时,使用当前资源的全局统计节点
- 非直接限流,使用关联资源的统计节点。
 
2.2.7.2、canPass
  在拿到上一步推断出的节点后,会调用canPass方法,这里的canPass也有不同的实现:
 
   对应控制台中的:
 
   这里涉及到滑动窗口,令牌桶,漏桶算法,会在后续进行说明。 Sentinel的难点不在于流程,而是算法。
2.2.8、DegradeSlot
  DegradeSlot的作用是熔断降级控制。也是 Sentinel 的核心功能之一:
   在tryPass方法中,会对逐条规则进行校验,如果此时的断路器处于打开状态,
 
   并且超过了熔断时间,会修改状态为半开。
 
  熔断降级中有一个重要的概念,也就是断路器。在Sentinel 1.8 版本之后,断路器有三种状态,都记录在CircuitBreaker的内部State枚举类中:
   在这里简单的说一下三种状态的转换:
- 正常情况下,断路器处于关闭状态,所有请求正常通过。
- 当请求触发了降级条件(如异常比例过高、RT过大) 后,断路器会进入打开状态,在接下来的熔断时长内(如 10 秒),所有请求都被拒绝(降级)。
- 当熔断时长结束后,下一个请求到达时,断路器进入半开状态:- 如果该请求再次触发降级条件,断路器重新回到打开状态。
- 如果该请求通过且正常,断路器会恢复为关闭状态。
 
- 如果该请求再次触发降级条件,断路器重新回到
Closed → [触发降级条件] → Open → [熔断时长结束,下一请求] → Half-Open
↑ ↓
└────── [探测失败] ←──── [探测成功] ←──────────────┘
总结
  本篇介绍了Sentinel 实现控制台功能,在服务端的实现原理:通过AOP + 责任链模式实现。并且在调用目标方法时,为每一个请求都创建一份责任链,放入缓存,依次调用。
   后面几个责任链的实现,在规则校验不通过时,都会抛出异常,而真正处理的逻辑,在StatisticSlot的catch中,以及SentinelResourceAspect#invokeResourceWithSentinel的entry.exit中,包括处理断路器的状态。
下一篇:Sentinel核心源码分析(下)