写点什么

限流功能实现原理

  • 2023-06-10
    湖南
  • 本文字数:3674 字

    阅读完需:约 12 分钟

限流一般指的是按 QPS/Threads 限流,除 QPS/Threads 限流外,还有热点参数限流、集群限流。本篇主要分析 Sentinel 限流功能的实现原理及几种流量效果控制器的实现原理。

限流功能实现原理

Sentinel 由 ProcessorSlot、Checker、Rule、RuleManager 组合完成限流、熔断降级、黑白名单限流、系统自适应限流及热点参数限流。


ProcessorSlot 是实现一系列功能的入口,负责调用 Checker 检查是否可以放行当前请求;


Checker 则根据资源名称从 RuleManager 中拿到为该资源配置的规则,从 Node 中获取实时的指标数据并与规则配置的阈值对比,若达到规则的阈值,则抛出 BlockException。


以限流功能的实现为例:FlowSlot 作为实现限流功能的入口,是否会拒绝请求将交由 FlowRuleChecker 判断;FlowRuleChecker 则根据资源的实时指标数据检查是否达到限流规则的阈值,只要达到某个限流规则的阈值,就抛出 FlowException(BlockException 的子类)或采用流量效果控制器处理超出阈值的流量。

限流规则

Sentinel 在最初的框架设计上,将是否允许请求通过的判断行为交给 Rule 实现,所以将 Rule 定义成了接口。Rule 接口只定义了一个 passCheck 方法,即判断当前请求是否被允许通过。Rule 接口的定义如下。

  • context:当前调用链上下文。

  • node:当前资源的 DefaultNode 实例。

  • count:一般为 1,用在令牌桶算法中表示需要申请的令牌数,用在 QPS 统计中表示一个请求,用在并行占用线程数统计中表示一个线程。

  • args:方法参数,用于实现热点参数限流降级。


因为规则是围绕资源配置的,一个规则只对某个资源起作用,所以 Sentinel 提供了一个抽象规则配置类 AbstractRule。AbstractRule 类的定义如下:

  • resource:资源名称,规则的作用对象。

  • limitApp:只对哪个或哪些调用来源生效,若为 default,则不区分调用来源。


Rule、AbstractRule 与其他实现类的关系如图所示:

FlowRule 是限流规则配置类,FlowRule 类继承了 AbstractRule 类并实现了 Rule 接口,源码如下:

  • grade:限流阈值类型,QPS 或并行占用线程数。

  • count:限流阈值。

  • strategy:基于调用关系的限流策略。

  • refResource:引用的资源,仅在配置了 strategy 的情况下使用。

  • controlBehavior:流量控制效果,如直接拒绝、冷启动、匀速排队。

  • warmUpPeriodSec:冷启动时长,单位为秒。

  • maxQueueingTimeMs:最大排队等候时间,单位为毫秒。

  • controller:流量效果控制器。


从源码中可以看出,FlowRule 类实现 Rule 接口的 passCheck 方法只返回了 true,这是因为 passCheck 方法的逻辑并不是由 FlowRule 类实现的。


Rule 定义的行为应该只是 Sentinel 在最初搭建框架时定义的约定,Sentinel 自己也并没有完全遵守这个约定,有很多规则并没有将 passCheck 方法交给 Rule 实现,而是交给了 Checker 实现。Checker 是后续引入的,用于替代 Rule 的 passCheck 方法。

限流规则加载器

在 Sentinel 中,用来管理规则配置的类都以“规则类的名称+Manger”命名,除此之外,并没有对规则管理器有任何行为上的约束。


用来加载和缓存限流规则配置的类为 FlowRuleManager,其部分源码如下:

  • flowRules:用于缓存限流规则配置,使用 ConcurrentMap 缓存,key 为资源的名称,value 是一个 FlowRule 集合。

  • getFlowRuleMap:提供给 FlowSlot 获取配置的内部 API。

  • loadRules:加载和更新限流规则的 API,该方法会将参数传递进来的限流规则集合转换为 Map,然后先清空 flowRules 当前缓存的限流规则配置,再将新的限流规则配置写入 flowRules 中。


Sentinel 支持针对同一个资源配置多种限流规则,只要有一个限流规则达到限流阈值即可触发限流,因此 FlowRuleManager 使用集合缓存同一资源的多个限流规则。

限流处理器插槽

FlowSlot 是实现限流功能的入口,它作为 ProcessorSlot 被插入 ProcessorSlotChain 中,在 entry 方法中调用 Checker 可以判断是否需要拒绝当前请求,若需要拒绝请求,则抛出 BlockException。FlowSlot 的源码如下:

FlowSlot 在构造方法中创建 FlowRuleChecker 实例,并在 entry 方法中调用 FlowRuleChecker 实例的 checkFlow 方法,判断是否需要拒绝当前请求。


在调用 FlowRuleChecker 实例的 checkFlow 方法时,传入了一个 Function 接口实例,FlowRuleChecker 可以调用该 Function 实例的 apply 方法,从 FlowRuleManager 中获取资源的所有限流规则配置。


当然,最终还是调用 FlowRuleManager#getFlowRuleMap 方法从 FlowRuleManager 中获取资源的所有限流规则配置。

限流规则检查器

在 Sentinel 中,约定 Checker 类需要以“规则类的名称+Checker”命名,但是没有约定 Checker 类必须具有哪些行为。


FlowRuleChecker 负责判断是否需要拒绝当前请求,因为 FlowRuleChecker 类的源码很多,所以我们按过程分析用到的每个方法。


由 FlowSlot 调用 checkFlow 方法,该方法的源码如下:

  1. 调用 FlowSlot 传递过来的 ruleProvider 的 apply 方法获取当前资源的所有限流规则。

  2. 遍历限流规则,只要有一个限流规则达到限流阈值即可抛出 FlowException。

  3. 调用 canPassCheck 方法判断是否放行当前请求。


提示:FlowException 类是 BlockException 类的子类。使用 FlowException 的目的是标志当前请求因为达到限流阈值而被拒绝。


canPassCheck 即“can pass check”,意思是检查是否允许通过,后面将统一使用 canPassCheck 表示“检查是否允许当前请求通过”。若 canPassCheck 方法返回 true,则说明允许当前请求通过,否则不允许当前请求通过。canPassCheck 方法的源码如下:

  1. 指定当前限流规则只对哪个调用来源生效,默认为“default”,即不限定调用来源。

  2. 指定是否是集群限流模式,如果是集群限流模式,则调用 passClusterCheck 方法完成 canPassCheck。

  3. 如果是非集群限流模式,则调用 passLocalCheck 方法完成 canPassCheck。


我们暂时只讨论非集群限流模式。非集群限流模式会调用 passLocalCheck 方法,该方法的源码如下:

调用关系限流策略(strategy)有如下 3 种取值:

  • STRATEGY_DIRECT:按资源限流。

  • STRATEGY_RELATE:按引用的资源限流。

  • STRATEGY_CHAIN:按调用链限流。


提示:引用其他资源的指标数据作为是否限流的依据,通俗来说,就是使用其他资源的指标数据限流,当对立资源的并发量高时,就限流当前资源,让对立资源多处理一些请求,等对立资源并发量降低了,就取消限流当前资源。


其中,根据调用来源和调用关系限流策略选择 Node,就是根据限流规则配置的 limitApp 与 strategy 选择一个 Node,这两个字段可以组合成 9 种情况。使用 selectNodeByRequesterAndStrategy 方法实现根据限流规则配置的 limitApp 与 strategy 选择 Node,源码如下:

如源码所示,limitApp 表示限流规则仅对指定调用来源生效,strategy 表示限流规则使用的调用关系限流策略,而 origin 表示当前请求的调用来源。


如果仅按 limitApp 划分,可以划分为 3 种情况,对应代码中的 3 个注释,而每种情况都会因 strategy 的取值不同再分裂出 3 种情况,下面按先粗分后细分的步骤分析所有可能。

针对指定调用来源限流

在按指定调用来源限流的基础上,可以按资源限流、按引用的资源限流或按调用链限流,这 3 种情况分别对应 strategy 的不同取值:

  • STRATEGY_DIRECT:在按指定调用来源限流的基础上按资源限流,取对应调用来源的 StatisticNode。

  • STRATEGY_RELATE:在按指定调用来源限流的基础上按引用的资源限流,取引用资源对应调用来源的 StatisticNode。

  • STRATEGY_CHAIN:在按指定调用来源限流的基础上按调用链限流,取当前资源的 DefaultNode。

针对所有调用来源限流

当 limitApp 为 default 时,针对所有调用来源限流。在不区分调用来源限流的基础上,可以按资源限流、按引用的资源限流或按调用链限流,这 3 种情况分别对应 strategy 的不同取值:

  • STRATEGY_DIRECT:在按所有调用来源限流的基础上按资源限流,取当前资源的 ClusterNode。

  • STRATEGY_RELATE:在按所有调用来源限流的基础上按引用的资源限流,取引用资源的 ClusterNode。

  • STRATEGY_CHAIN:在按所有调用来源限流的基础上按调用链限流,取当前资源的 DefaultNode。

既不针对所有调用来源限流,也不针对当前调用来源限流

当 limitApp 为 other 时,说明当前规则既不针对所有调用来源限流,也不针对某个调用来源限流,如果此时围绕该资源配置的所有限流规则都没有针对当前调用来源限流,则当前规则才会生效。

  • STRATEGY_DIRECT:取 origin 对应的 StatisticNode,按调用来源限流。

  • STRATEGY_RELATE:取引用资源的 ClusterNode,按引用的资源限流。

  • STRATEGY_CHAIN:取当前资源的 DefaultNode,按调用链限流。


从 selectNodeByRequesterAndStrategy 方法中可以看出,Sentinel 之所以为每个资源统计区分不同调用来源的指标数据,是为了能够实现丰富的限流策略。


每个调用来源服务对同一个资源的访问频率是不同的,针对调用来源限流可以限制并发量较高的调用来源服务的请求,而对并发量低的调用来源服务的请求可以不限流,或者可以对一些并没有那么重要的调用来源服务限流。


当两个资源之间具有资源争抢关系的时候,使用 STRATEGY_RELATE 调用关系限流策略可以避免多个资源之间过度地争抢同一资源。例如,查询订单信息和用户下单这两个操作分别需要读和写数据库订单表的资源,如图所示。

我们可以给执行读表操作的资源设置限流规则以实现写优先的目的。查询订单信息的资源会根据用户下单资源的实时指标数据限流,当写表操作过于频繁时,读表操作的请求就会被限流。

用户头像

加VX:bjmsb02 凭截图即可获取 2020-06-14 加入

公众号:程序员高级码农

评论

发布
暂无评论
限流功能实现原理_互联网架构师小马_InfoQ写作社区