面试漫谈(十二)- Spring组件篇

Posted by YiBo on December 19, 2020

reference:

https://juejin.cn/post/6906302891875647495

前记

这几天又陆续面了2个大厂,一面都顺利的过了,等待这我的是未知的狂风暴雨

Sentinel是如何做限流的

一般来说,经常会通过@SentinelResource来标记一个方法,可以将这个被标记的方法看成是一个sentinel资源。以@SentinelResource为入口,找到其切面,看看切面拦截后的所做的工作

进入sentinelResource切面后,会执行SphU.entry方法,在这个方法中会对被拦截方法做限流和熔断的逻辑处理,如果触发熔断和限流,会抛出BlockException,我们可以指定BlockHandler方法来处理这个异常。而对于业务上的异常,我们可以配置fallBack方法来处理被拦截方法调用产生的异常

在SphU.entry方法中,Sentinel实现限流,熔断功能的流程如下:

  • 获取Sentinel上下文(Context)

    Context包含了资源调用的节点和Entry信息

    1. Context:进入Sentinel逻辑时,首先获取当前线程的Context,当任务执行完毕后,被清除当前线程的Context。Context代表调用链路上下文,贯穿一次调用链路中的所有entry。Context维持这入口节点(entranceNode)本此调用链路的当前节点(curNode)调用来源(origin)等信息。Context名称即为调用链路入口名称
    2. Node:是对一个@SentinelResource标记的资源的统计包装,Context中记录本当前线程资源的入口节点,可以通过入口节点的childList 追溯资源的调用情况,每个节点都对应一个@sentinelResource标记的资源以及其统计数据,例如passQps blockQps rt等数据
    3. Entry:是Sentinel中用来表示是否通过限流的一个凭证,如果不通过会抛出BlockException。另外,他保存了本地执行entry方法的一些基本新,包括资源的Context Node 以及对应的责任链等信息、后续完成资源调用后,还需要获得这个entry去执行一些善后工作,包括退出entry对应的责任链,完成节点的一些统计信息更新,清楚当前线程的Context信息等

    Context是线程持有的,利用ThreadLocal

  • 获取资源对应的责任链

    默认的责任链中处理节点包括 NodeSelectorSlot ClusterBuilderSlot StatisticSlot FlowSlot等,调用连(ProcessorSlotChain)和其中包含了所有Slot都实现了ProcessorSlot接口,采用责任链的模式执行各个节点的处理逻辑,并调用下一个节点。每个资源对应一条单独的责任链。先从缓存获取,没有则新建

  • 生成资源调用凭证(Entry)

    生成entry的是CtEntry。他的构造函数包含资源包装(ResourcewWrapper),资源对应的责任链以及当前线程的Context。CtEntry是一个双向链表,构建了sentinel资源的调用链路

  • 执行责任链中各个节点

    责任链和其中的Slot都实现了ProcessorSlot,责任链的entry方法会依次执行责任链各个Slot,所以下面就进入了责任链的各个Slot

    1. NodeSelectorSlot 获取当前资源对应的Node,构建节点调用树

      有三个作用:

      • 在资源对应的调用链执行时,获取当前Context对应的NOde,这个Node代表这个资源的调用情况
      • 将获取到的node设为当前node,添加到之前的node后面,形成树状调用路径
      • 触发下一个Slot执行

      NodeSelectorSlot获取资源对应的Node时,是用的Context的name,而不是SentinelResource的name。因为一个资源对应一个责任链。但是进入一个资源调用的Context却是不同的。比如如果用Dubbo Filter方式,生成的Context的name是Dubbo的接口限定名或者方法限定名,所有这样一种情况,不同的dubbo接口进来,都调用了同一个@SentinelResource标记的方法,那么折线方法对应的SentinelResource的在执行时对应的Context就是不同的

    2. ClusterBuilderSlot–聚合相同资源的不同Context的node

      ClusterNode的获取是以资源名为key,ClusterNode将会成为当前node的一个属性,主要为了聚合同一个资源不通Context情况下的多个node

    3. StatisticSlot – 资源调用统计

      主要负责资源调用统计信息的计算和更新

    4. FlowSlot – 限流判断

      上一步StatisticSlot对相关资源调用做的统计,在FlowSlot限流判断时会得到使用

      • 首先获取资源对应的限流规则
      • 根据规则检查是否被限流:主要是对qps计数进行先查

总结一下上述的步骤:NodeSelectorSlot用于获取资源对应的node,并构建node调用树,将SentinelSource的调用链路以Node Tree的形式组起来,ClusterBuilderSlot为当前node创建对应的CLusterNode,聚合相同资源对应的不同的Context的node,后续限流依据就是这个ClusterNode。ClusterNode继承自StatisticNode,记录着相应资源处理的一些统计数据,StatisticSlot用于更新资源调用的相关计数,FlowSlot更具Node的调用计数,判断是否限流

最后还有个收尾工作,Entry.exit()方法

最后再总结一下

  • 三大组件Context、Entry、Node,是Sentinel的核心组件,各类信息及资源调用情况都由这三大类持有;
  • 采用责任链模式完成Sentinel的信息统计、熔断、限流等操作;
  • 责任链中NodeSelectSlot负责选择当前资源对应的Node,同时构建node调用树;
  • 责任链中ClusterBuilderSlot负责构建当前Node对应的ClusterNode,用于聚合同一资源对应不同Context的Node;
  • 责任链中的StatisticSlot用于统计当前资源的调用情况,更新Node与其对用的ClusterNode的各种统计数据;
  • 责任链中的FlowSlot根据当前Node对应的ClusterNode(默认)的统计信息进行限流;
  • 资源调用统计数据(例如PassQps)使用滑动时间窗口进行统计;
  • 所有工作执行完毕后,执行退出流程,补充一些统计数据,清理Context。