Sentinel核心源码分析(上)

文章目录


前言

Sentinel作为Spring cloud alibaba中流控的组件,在微服务架构中也有广泛的应用。其核心源码主要体现在客户端。客户端在启动时,和Nacos类似,也会将自己的信息注册到服务端。而服务端的页面上配置各种规则时,实际上也是将信息发送到了客户端。

我们最常使用的@SentinelResource注解:

底层也是基于AOP + 责任链模式实现的。**Sentinel的难点不在于处理流程,而在于限流的算法。**本篇仅介绍Sentinel责任链的核心流程。


一、客户端与Spring Boot整合

在Spring Boot项目中,如果需要引入Sentinel,通常需要在pom文件中加入:

xml 复制代码
        <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方法。也就是说,是每一个加入了注解的方法,都有一个对应的责任链,并且只在应用启动后该方法第一次被调用时初始化。

最终调用的是DefaultSlotChainBuilderbuild方法:

在该方法中,主要做了两件事:

通过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

ClusterBuilderSlotNodeSelectorSlot是类似的,它的作用是构建统计节点的聚合关系。但是NodeSelectorSlot 是按调用链统计,ClusterBuilderSlot 是按资源维度统计。

2.2.3、LogSlot

LogSlot的作用,是在后续的责任链调用过程中出现异常时,进行日志的记录,体现在它的try...catch中:

2.2.4、StatisticSlot

StatisticSlot是先将请求放行到后续的责任链,在后续的责任链调用完成后,再去进行统计资源的调用情况的操作。例如记录 QPS(每秒请求数)、RT(响应时间)、线程数、异常数等。

包括抛出了各种异常之后的记录,这些记录都是执行降级、限流等控制的基础数据来源。

2.2.5、AuthoritySlot

AuthoritySlot是进行授权规则的检查,例如黑白名单:


简单回顾一下黑白名单的使用,首先需要在sentinel控制台的授权规则选项卡进行配置:

这里的资源名,是http请求的路径,而流控应用,可以是特定的ip,也可以是请求路径:

java 复制代码
@Component
public class IPLimiter implements RequestOriginParser {

	/**
	*	获取当前服务实例的ip
	*/
    @Override
    public 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枚举类中:

在这里简单的说一下三种状态的转换:

  1. 正常情况下,断路器处于关闭状态,所有请求正常通过。
  2. 当请求触发了降级条件(如异常比例过高、RT过大) 后,断路器会进入打开状态,在接下来的熔断时长内(如 10 秒),所有请求都被拒绝(降级)。
  3. 当熔断时长结束后,下一个请求到达时,断路器进入半开状态:
    • 如果该请求再次触发降级条件,断路器重新回到打开状态。
    • 如果该请求通过且正常,断路器会恢复为关闭状态。

Closed → [触发降级条件] → Open → [熔断时长结束,下一请求] → Half-Open

↑ ↓

└────── [探测失败] ←──── [探测成功] ←──────────────┘

总结

本篇介绍了Sentinel 实现控制台功能,在服务端的实现原理:通过AOP + 责任链模式实现。并且在调用目标方法时,为每一个请求都创建一份责任链,放入缓存,依次调用。

后面几个责任链的实现,在规则校验不通过时,都会抛出异常 ,而真正处理的逻辑,在StatisticSlot的catch中,以及SentinelResourceAspect#invokeResourceWithSentinelentry.exit中,包括处理断路器的状态。

下一篇:Sentinel核心源码分析(下)

相关推荐
李慕瑶4 分钟前
Scala语言的移动UI设计
开发语言·后端·golang
Asthenia041213 分钟前
用Pandas轻松搞定Excel数据提取:新手也能学会的Python技巧
后端
uhakadotcom15 分钟前
Google Cloud Dataproc:简化大数据处理的强大工具
后端·算法·面试
兰亭序咖啡24 分钟前
学透Spring Boot — 018. 优雅支持多种响应格式
java·spring boot·后端
审计侠31 分钟前
Go语言-初学者日记(八):构建、部署与 Docker 化
开发语言·后端·golang
AskHarries43 分钟前
如何开通google Free Tier长期免费云服务器(1C/1G)
后端
码界筑梦坊1 小时前
基于Django的二手交易校园购物系统
大数据·后端·python·信息可视化·django
东方珵蕴1 小时前
Logo语言的区块链
开发语言·后端·golang
烛阴1 小时前
从零到RESTful API:Express路由设计速成手册
javascript·后端·express
uhakadotcom1 小时前
Mars与PyODPS DataFrame:功能、区别和使用场景
后端·面试·github