什么是限流器以及常见的服务限流算法
扫描二维码
随时随地手机看文章
作者:lotusgrm在开发高并发系统的时候,我们一般通过三种方式去保障系统服务稳定:服务降级、服务限流和缓存。对于缓存在实际开发中使用的比较多,这个是大家的共识没有什么异议的。今天我们的主角是服务限流,在此之前我们先来看下服务限流 和 服务降级 的区别:
服务降级
服务降级是在服务器压力陡增的情况下,利用现有的资源,关闭一些服务接口或者页面,以此来释放服务器资源保证核心任务的正常运行服务限流
限流本质上是减少流量的访问,而服务的处理能力不变;而对于服务降级来说是降低了部分服务的处理能力,增加另一部分服务处理能力,访问量不变本篇文章中说到的服务限流不是 nginx 层面的限流,而是业务代码中的逻辑限流。
那么,我们为什么要服务限流?
从服务调用方来说,可以把服务简单的划分为以下几种类型的服务1.与用户打交道的服务
比如 web 服务,对外 API,这种类型的服务有以下几种可能会导致服务器压力变大:
-
用户增长过快
-
热点事件
-
恶意的攻击/刷单
-
爬虫
这些情况都是无法预知的,我们不知道什么时候请求流量会突增,如果真的要是碰到这种情况,扩容根本是来不及的
2.对内的 RPC 服务
一个服务 A 的接口可能会被其他 BCDE 多个服务调用,如果其中一个服务的流量突增比如 B 服务,导致 A 服务压力变大甚至挂掉,那么这个时候也不能给 CDE 服务提供服务
这种情况在实际开发中时常发生
常见的限流算法
下面我们来看几种实际业务场景中常用的几种限流算法:计数器算法、漏桶算法、令牌桶算法
计数器算法
计数器算法应该是最简单,最容易想到的限流策略。限流的本质是限制某一个 API 在某个维度单位时间内处理请求的次数。我们可以设置一个计数器对某一时间段内的请求进行计数,当请求量超过事先设定好的阈值,拒绝用户的请求。比如限流的 QPS 是100,算法的实现思路是从第一个请求进来开始计数,在接下来的 1s 内,每来一个请求计数器自动加 1,如果累加的数字超过 100,那么后续的请求就会被拒绝,等到 1s 结束后,计数器开始重置为 0,重新开始计数。这种方式存在弊端
弊端1:如果在 1s 的前 10ms 请求数已经达到了 100,那么剩下的 990ms,服务调用方就要眼巴巴的看着请求被拒绝
弊端2:如果有一个恶意的用户在 999ms 的时候瞬间发送了 100 次请求,并且又在 1000ms 时瞬间发送了 100 次请求,那么其实这个用户在 1s 内发送了 200 次请求。利用时间窗口的重置节点处突发请求,可以瞬间超过我们服务本身能承受的压力,瞬间压垮服务
漏桶算法
漏桶算法是限流方面比较经典的算法。可以把该算法联想成一个具体的漏桶模型,漏桶始终以一个恒定的速率往外排水,如果桶被装满的话则后来涌入的水会溢出。对应到接口限流来说,用户的请求可以看做是水,不管用户的请求量有多大多不均衡,能够被处理的请求速率是恒定的,而且能够接受的请求数也是有上限的,超出上限的请求会被拒绝,在算法实现方面可以准备一个队列来作为漏桶的实现,用这个队列保存请求,通过一个线程池定期从队列中获取请求并执行,也可以一次性获取多个并发请求,如下图:
令牌桶算法
在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌才有机会继续执行,否则请求选择等待或者服务直接拒绝请求放令牌这个动作是持续不断的进行,如果桶中令牌数量达到上限,就丢弃令牌,所以,会存在这样的一种情况,桶中一直有大量的令牌,这时进来的请求就可以直接拿到令牌执行。比如我们把 QPS 设置成 100,那么限流器完成初始化 1s 后,桶中已经有 100 个令牌,这时候服务还未完全启动好,等启动完成功对外提供服务时,该限流器可以抵挡瞬时的 100 个请求。所以,当桶中没有令牌时请求才会等待或者被拒绝,当没有请求申请令牌时,那么这个令牌桶会溢出。在这里令牌桶类似一个 buffer,请求密度比较低或者处于冷却状态下,令牌桶会溢出,当请求流量突增时,则过去积累的结余资源直接可以拿来借用,从这里可以看出这点弥补了漏桶算法的不足
比如为了限制某个资源被某个用户访问的次数,10 s 只能访问 3 次,或者一天只能访问 100 次,这种需求通过单机限流是无法实现的,这时就需要通过集群限流的方式实现