Skip to content

流量控制

louyuting edited this page Aug 28, 2020 · 17 revisions

流控规则

流控规则的定义如下, FlowRule

type FlowRule struct {
	// ID represents the unique ID of the rule (optional).
	ID uint64 `json:"id,omitempty"`

	// Resource represents the resource name.
	Resource string `json:"resource"`
	// LimitOrigin represents the target origin (reserved field).
	LimitOrigin string     `json:"limitOrigin"`
	MetricType  MetricType `json:"metricType"`
	// Count represents the threshold.
	Count            float64          `json:"count"`
	RelationStrategy RelationStrategy `json:"relationStrategy"`
	ControlBehavior  ControlBehavior  `json:"controlBehavior"`

	RefResource       string `json:"refResource"`
	WarmUpPeriodSec   uint32 `json:"warmUpPeriodSec"`
	MaxQueueingTimeMs uint32 `json:"maxQueueingTimeMs"`
	// ClusterMode indicates whether the rule is for cluster flow control or local.
	ClusterMode      bool              `json:"clusterMode"`
	ClusterConfig    ClusterRuleConfig `json:"clusterConfig"`
	WarmUpColdFactor uint32            `json:"warmUpColdFactor"`
}

一条流控规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:

  • Resource:资源名,即规则的作用目标;
  • MetricType: 指标类型,支持基于并发数和QPS作为统计指标;
  • Count: 流控阈值;
  • RelationStrategy: 调用关系限流策略,Direct表示使用当前resource做流控;AssociatedResource表示使用关联的resource做流控,关联的resource在 RefResource 定义;
  • RefResource: 关联的resource;
  • ControlBehavior: 流量控制效果,目前支持三种:Reject(直接拒绝)、WarmUp(预热)、Throttling(匀速排队);
  • WarmUpPeriodSec: 预热的时间长度,该字段仅仅对 WarmUp 策略生效;
  • MaxQueueingTimeMs: 匀速排队的最大等待时间,该字段仅仅对 Throttling 策略生效;
  • WarmUpColdFactor: 预热的因子,默认是3,该值的设置会影响预热的速度,该字段仅仅对 WarmUp 策略生效;

基于并发/QPS的流控

流量控制主要有两种统计类型,一种是统计并发协程数,另外一种则是统计 QPS。类型由 FlowRule 的 MetricType 字段来定义。

基于并发做流量控制

并发数控制用于保护业务协程不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的协程数占用,极端情况下甚至导致协程耗尽。为应对太多协程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同协程池来隔离业务自身之间的资源争抢(协程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置

注意:对于基于并发数做流控时,流控的策略只支持 Reject

基于QPS的流量控制

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。对应 FlowRule 中的 ControlBehavior 字段。

  1. 直接拒绝的方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

  2. WarmUp方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。这块设计和Java类似,可以参考限流 冷启动 通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:

  3. 匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。该方式的作用如下图所示:

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

以下规则代表每 100ms 最多通过一个请求,多余的请求将会排队等待通过,若排队时队列长度大于 500ms 则直接拒绝:

{
	Resource:          "some-test",
	MetricType:        flow.QPS,
	Count:             10, // 请求的间隔控制在 1000/10=100 ms
	ControlBehavior:   flow.Throttling, // 流控效果为匀速排队
	MaxQueueingTimeMs: 500, // 最长排队等待时间
}

MaxQueueingTimeMs 设为 0 时代表不允许排队,只控制请求时间间隔,多余的请求将会直接拒绝。

基于调用关系的流量控制

Sentinel 支持关联流量控制策略。当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢。

核心接口

在流控模块(flow)中,Sentinel 会为每条流控规则(FlowRule)生成相应的流量调配器(TrafficShapingController)。TrafficShapingController 由两部分构成:

  • TrafficShapingCalculator: 根据规则的计算策略计算出当前的流量阈值(如部分策略在一段时间内逐步抬升 QPS 阈值)
  • TrafficShapingChecker: 根据阈值执行相应的检查和调配策略,返回 base.TokenResult 指示如何进行调配