限流介绍
1、基础概念
限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。常用的限流算法有计数器、令牌桶和漏桶,而比较常用的为后面2种。限流虽然会影响部分用户的使用体验,但是却能在一定程度上报障系统的稳定性,不至于崩溃。
如何你是一个设计人员,系统准备设计集成限流功能,需要我们从多个维度去思考,因为每个系统业务场景、应用深度都是不一样的,没有一个通用的方案,需要结合当前系统自身情况综合分析,这里整理一张思维导图,至少会让你对限流有一个宏观的认知。
而互联网上类似需要限流的业务场景也有很多,比如电商系统的秒杀、微博上突发热点新闻、双十一购物节、12306抢票等等。这些场景下的用户请求量通常会激增,远远超过平时正常的请求量,此时如果不加任何限制很容易就会将后端服务打垮,影响服务的稳定性。
限流算法
1、计数器
基本实现思路
它是将时间划分成一个一个固定的时间段,然后时间段中绑定一个计数器,记录这个时间段内的请求数,当时间段内的请求数到达设定阈值之后,再有请求过来,就直接拒绝;当达到时间段结束点后,即在每个时间段的起始位置,清0计数器
实现关键要点
计数器限流 | 时间窗口大小 | 用于统计基数的时间窗口、例如10秒 |
限流的阈值 | 允许通过的最大值,例如10秒内允许100 个请求 | |
窗口结束时间 | 窗口失效的时间 |
时间窗口
类型 | 实现思路 | 实现步骤 |
固定时间窗口 | 某一个接口或服务1s最多只能接收100个请求,那么我们就会设置其限流为100QPS。该算法的实现思路非常简单,维护一个固定单位时间内的计数器,如果检测到单位时间已经过去就重置计数器为零 | 1、时间线划分为多个独立且固定大小窗口; 2、落在每一个时间窗口内的请求就将计数器加1; 3、如果计数器超过了限流阈值,则后续落在该窗口的请求都会被拒绝。但时间达到下一个时间窗口时,计数器会被重置为0。 |
| ||
滑动时间窗口 | 滑动时间窗口算法是对固定时间窗口算法的一种改进,这词被大众所知实在TCP的流量控制中。固定窗口计数器可以说是滑动窗口计数器的一种特例 | 1、将单位时间划分为多个区间,一般都是均分为多个小的时间段; 2、每一个区间内都有一个计数器,有一个请求落在该区间内,则该区间内的计数器就会加一; 3、每过一个时间段,时间窗口就会往右滑动一格,抛弃最老的一个区间,并纳入新的一个区间; 4、计算整个时间窗口内的请求总数时会累加所有的时间片段内的计数器,计数总和超过了限制数量,触发限流。 |
|
注意使用 "固定时间窗口" 在实现上比较简单,但有也有比较大的问题,比如
-
产生突刺效应
-
在时间窗口临界出会承受过量流量压力
2、漏桶算法
基本实现思路
漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率
在算法实现方面,可以准备一个队列,用来保存请求,另外通过一个线程池定期从队列中获取请求并执行,可以一次性获取多个并发执行。
实现关键要点
漏桶算法 | 流量速率 | 可以任意速率流入水,因为外部流量进入是未知的 |
桶的大小 | 桶的大小是固定的,可以根据业务调整大小 | |
流出速率 | 漏出去的水都是固定的,可以根据自己系统压力测试指标调整大小 |
优势
漏桶算法在限流方面的作用主要就是流量整形。对于外部进来的流量大小不可预知,但是漏桶的流出速率是一个可控制的恒定的均匀的速率,从而达到流量整形的目的。并且使系统不被压垮。
弊端
因为是恒定的流出速率,桶是固定大小,当流量速率大于桶的容量大小时,请求就会被丢弃,无法应对流量洪峰,对业务系统瞬时突发的流量应对比较弱。
3、令牌桶算法
基本实现思路
令牌桶算法是流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。如果获取到令牌则放行。
产生令牌:周期性的以速率R向令牌桶中增加令牌,桶中的令牌不断增多。如果桶中令牌数已到达C,则丢弃多余令牌。
消耗令牌:输入数据包会消耗桶中的令牌。在网络传输中,数据包的大小通常不一致。大的数据包相较于小的数据包消耗的令牌要多。
判断是否通过:输入数据包经过令牌桶后的结果包括输出的数据包和丢弃的数据包。当桶中的令牌数量可以满足数据包对令牌的需求,则将数据包输出,否则将其丢弃。
实现关键要点
结合令牌桶算法上面的那个图,我们做出一些假设,在技术实现上梳理了一些具体指标
设计指标 | 标识符号 | 场景值 |
假设放入令牌的速率 | R | 每秒放入100个令牌 |
假设令牌桶的总容量 | C | 桶最大容量可以放入500个令牌 |
上一次请求处理的时间 | AT | |
上一次请求处理完毕剩余令牌数 | W | |
当前请求 | B | |
当前请求的时间 | BT | 当前时间 |
当期请求对应的令牌数 (WB) = (BT - AT)* R
当前桶中的令牌总数 = WB + W
因为受桶总容量的限制,令牌总数是不能超过桶的大小 = Min(WB + W ,C)
具体场景如下:、桶的容量为500、每秒放入100个令牌、相当于每秒可以处理100个请求,理想情况下大约5秒放满。如果根据企业系统运行情况评估出,系统每秒只能处理100个请求,那么令牌放入的速率可以设置为>=100,假设5秒内没有请求,那么桶中放满最大令牌数500个,如果某一个时刻系统突然大量请求,那么就可以应对突发流量。
结语
上面我们对限流实践有了一个整体认识,同时我们也对限流算法特性和实现原理有了一定了解,这样我们能根据业务场景做出一些技术选型,但这还远远不够,虽然行业中有一些成熟的方案,如果我们需要改变一下,需要做一些个性化定时,那我们就需要自己去改动限流的实现方式,所以下一篇我们会对开源限流工具的源码进行剖析,和具体的实现,这样我们就能基于现有的基础上举一反三。