阅读 655

过载保护 | 熔断和限流

本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

过载保护的重要性

什么是过载保护呢?所谓过载保护,是指负载超过系统的承载能力时,系统会自动采取保护措施,确保自身不被压垮

过载保护有多重要?12306 网站大家都用过吧?不知道你还记不记得刚开始网络订票的时候,每次春运抢票,12306 网站都会卡顿、瘫痪。后来它是怎么改进的呢?

  • 用户登录时添加图片验证,防止抢票软件自动登录;

  • 当用户请求比较频繁的时候,系统出现提示“您的操作频率过快请稍后重试”;

  • 当流量过大时,系统会提示“系统繁忙,请稍后重试”;

正是通过这几条措施,12306 网站在春运期间很少发生被流量压垮的故障了。

为什么这一系列改进能确保 12306 的稳定运行呢?因为它采取了过载保护。比如,其中的第 1 点和第 2 点能够限流,第 3 点可以熔断。什么意思呢?接下来我和你一一介绍。

熔断的基本原理

什么是熔断?熔断就是在系统濒临崩溃的时候,立即中断服务,从而保障系统稳定避免崩溃。 它类似于电器中的“保险丝”或“断路器”,当电流过大的时候,“保险丝”会先被烧掉,断开电流,以免电路过热烧毁电器引起火灾。软件系统中的熔断也是如此,当系统满足某个判断条件时,就会拒绝处理请求,避免系统压力过大被压垮。

比如,我们在上一讲介绍的 Nginx 健康检查,当请求某个节点连续失败 5 次后,就停止向该节点转发请求,这其实也是一种熔断措施。

那么,熔断的判断条件都有哪些呢?

熔断的判断条件往往跟系统节点的承载能力和服务质量有关,比如 CPU 的使用率超过 90%,请求错误率超过 5%,请求延迟超过 500ms, 它们中的任意一个满足条件就会出现熔断。

这些条件有的可以直接观测,比如请求错误率超过 5%,可以通过统计最近 1 分钟内的请求结果得到。但更多的条件往往需要借助第三系统了。比如,运维系统、监控系统、微服务注册中心等,它们的熔断条件需要由第三系统收集各节点的负载信息,然后同步到系统的调用方。

限流的基本原理

前面我们了解了熔断的基本原理,接下来我给你介绍下限流的基本原理。

限流的原理跟熔断有点类似,都是通过判断某个条件来确定是否执行某个策略。比如,如果并发请求达到 1000 QPS,那么系统会拒绝或者延迟处理后续请求。它的目的是确保系统高效、稳定地运行,确保请求能够快速处理的同时,保障系统不被流量压垮。

限流通常是利用某种算法实现限流器,来达到限制流量的目的。通常,限流器中会有一个定时器,它主要用来定时更新与条件有关的资源。还有,每次请求也需要更新该资源。如果抽象成 Go 中的 interface 话,示例代码如下:

type Limiter interface{

  Take() bool

  Start()

}
复制代码

其中 Take 方法用于每次请求时调用,判断该请求是否可以继续进行而不触发限流。Start 方法主要用于启动定时器,通常是在工厂方法中创建限流器时调用。示例代码如下:

func NewLimiter(name string, params Params) Limiter {

  var l Limiter

  if name == "counterLimiter" {

    // 创建计数器限流器

    l = newCounterLimiter(params)

  } else if name == "bucketLimiter" {

    // 创建漏桶限流器

    l = newBucketLimiter(params)

  }

  if l != nil {

    l.Start()

  }

  return l

}
复制代码

限流算法有多种,这两个方法按照不同算法有不同的具体实现。

总的来说,限流算法主要有:计数器限流、滑动窗口限流、令牌桶限流、漏桶限流。 怎么理解呢?

计数器限流算法

计数器限流算法也叫固定窗口限流算法。它是如何实现的呢?

  • 首先,选定一个时间窗口作为一个周期,假设为 60 秒;

  • 第二步,设定 60 秒内允许通过的流量,如 100 个请求;

  • 第三步,每次请求,计数器都加 1;

  • 第四步,判断计数器数值是否超过 100 ,超过了就触发限流策略,如:拒绝或者延迟处理请求等;

  • 最后,如果时间过了 60 秒,则重置计数为 0,开始一个新的周期。

image.png

该限流算法的优点是实现简单,缺点是面对突发流量时不够精确。面对瞬时流量时,会存在资源利用率的剧烈抖动。

滑动窗口限流算法

滑动窗口限流算法是对计数器限流算法的优化。它的主要原理是将计数器限流算法中的一个周期拆分成很多等分,比如将 5 秒的周期拆成 5 个 1 秒,每次统计从当前时间开始过去 5 秒内的流量,每隔 1 秒往后滑动 1 秒。

image.png

由于将周期拆分成多个小的单位,相比计数器限流算法,滑动窗口限流算法对流量的统计和控制要更精确,资源利用率抖动更小。但它还是没有彻底解决因瞬时流量导致资源使用率抖动的问题。

那么,有没有办法解决这个问题呢?有,它就是我接下来要介绍的令牌桶限流算法和漏桶限流算法。

令牌桶限流算法

令牌桶算法的基本原理是,使用一个定时器以恒定速度往桶里颁发令牌,桶满了则丢弃多余令牌

image.png

在令牌桶算法中,一般只有拿到令牌的请求才会被处理,没拿到的将会被拒绝。这个过程就像景区的人工售票窗口售票,只有买到票了才能检票进入景区。这其中,令牌就是门票,令牌桶就是售票窗口,负责发令牌的线程就类似于售票员,处理请求的线程就是检票员。

漏桶限流算法

漏桶算法的原理跟令牌桶有点相似,只不过漏桶算法采用“生产者-消费者”模型。在“生产者”一端,所有请求进队列,队列满了则丢弃请求。在“消费者”一端,以恒定速度消费队列并处理请求。

image.png

举个例子:乒乓球教练将乒乓球放入到发球机中,乒乓球发球机能以固定速度发出乒乓球,球员可以以固定速度击打乒乓球。例子中的乒乓球相当于软件系统中的请求,教练相当于“生产者”,发球机相当于漏桶,而球员相当于“消费者”。

以上这几种限流算法中,流量控制效果从好到差依次是:漏桶限流 > 令牌桶限流 > 滑动窗口限流 > 计数器限流

其中,只有漏桶算法真正实现了恒定速度处理请求,能够绝对防止突发流量超过下游系统承载能力。不过,漏桶限流也有个不足,就是需要分配内存资源缓存请求,这会增加内存的使用率。而令牌桶限流算法中的“桶”可以用一个整数表示,资源占用相对较小,这也让它成为最常用的限流算法。

正是因为这些特点,漏桶限流和令牌桶限流经常在一些大流量系统中结合使用。比如秒杀系统中就同时使用了这两种限流算法。

文章分类
后端
文章标签