/zlimiter

A toolkit for rate limite,support memory and redis

Primary LanguageGoMIT LicenseMIT

为什么要限流

恶意的流量访问

​ 线上服务运行过程中,或多或少都会接收到恶意的流量访问,巨量的DDOS攻击可能会直接导致服务的崩溃,限流可以为后端服务提供一定程度的保护

服务处理能力有限

​ 有时由于硬件资源、服务并发处理能力有限,后端服务必须对访问流量加以限制,以过滤掉超过自身处理能力的访问

Open API服务

​ 对于一些Open API服务,例如百度地图开放平台、腾讯AI开放平台都提供了基于QPS的计费策略,该计费策略下同样需要限流的处理

限流算法

固定窗口限流

​ 固定窗口限流算法将所有请求划分到长度固定的时间窗口内,在每个窗口期中分别通过计数进行流量控制,窗口的切换将导致计数器的清零。如下图所示

所有请求被被划分到长度为2s的窗口中,窗口的切换会导致计数值的重置(1时刻计数值为0,3时刻计数值重置到0)。

​ 固定窗口限流实现简单,但没有考虑窗口之间的流量访问,无法应对空窗期,如图所示假设在窗口2中有4次访问,窗口3中有3次流量访问,但在4-6时间段内实际访问流量为7,滑动窗口限流机制可以解决此问题。

​ 固定窗口限流,原理简单,常用于一些OpenAPI的流控计费控制。

滑动窗口限流

​ 滑动窗口限流将整个请求时间划分到一个不断滚动的窗口中(参见tcp的滑动窗口机制),每个窗口期内的计数会考虑其相邻窗口中的计数处理,如下图所示:

​ 假设窗口持续时间为2,时间点n对应的窗口期实际为[n-2,n)(即时间点n的计数需要考虑[n-2,n)时间段内的访问量),n+1点对应的窗口期实际为[n-1,n+1)(n+1内的计数需要考虑[n-1,n+1)时间段内的访问量)

漏桶限流

​ 漏桶限流算法以"桶"对所有请求流量进行缓存,最终能够实现将请求以恒定速率放行,在漏桶限流中如果请求数目超过了最大请求限制,则应拒绝再提供服务,其处理如下图所示:

​ 漏桶限流常用于访问流量的均衡处理,无法应对突发流量的控制。

令牌桶限流

​ 令牌桶算法以固定的速率生成Token并存放在桶中(如果生成的token数目超过了桶的容量,则抛弃token,即桶中最多能够容纳的token数目是固定的),当请求到达时如果桶中有剩余token则对请求进行处理,如果桶中已经没有多余token则抛弃请求。其处理如下图所示:

限流方式

集中式限流

​ 对于具有集中入口服务的系统、简单的单机服务,如API Gateway等,可以进行"单点"的限流。集中式限流器一般都是基于内存的处理。

分布式限流

​ 对于一些多机部署的集群,可以采用分布式限流方式,如下所示:

分布式限流器一般采用redis+lua方式,利用了Lua脚本在redis中原子性。

zlimiter

zlimiter是一个基于golang(1.12.7)的支持集中式、分布式限流方式,支持固定窗口、滑动窗口、bucket、token限流算法的限流器。

在工程中启用使用zlimiter:

go get github.com/zzerroo/zlimiter

分布式限流基于redis,所有测试都基于5.0.5版本。此外,分布式版本基于Lua脚本,目前暂不支持redis集群。

zlimiter的使用

固定窗口限流

zlimiter支持集中式、分布式的固定窗口限流,创建相应限流器的代码如下:

创建集中式固定窗口限流器:

memLimit := zlimiter.NewLimiter(zlimiter.LimitMemFixWindow)

创建分布式固定窗口限流器:

redisLimit := zlimiter.NewLimiter(zlimiter.LimitRedisFixWindow, rds.RedisInfo{Address: "127.0.0.1:6379", Passwd: "test"})

滑动窗口限流

zlimiter中创建集中式、分布式滑动窗口限流代码如下:

创建集中式滑动窗口限流器:

memLimit := zlimiter.NewLimiter(zlimiter.LimitMemSlideWindow)

创建分布式滑动窗口限流器:

redisLimit := zlimiter.NewLimiter(zlimiter.LimitRedisSlideWindow, rds.RedisInfo{Address: "127.0.0.1:6379", Passwd: "test"})

Bucket限流

zlimter中创建集中式、分布式bucket桶限流器的代码如下:

创建集中式bucket窗口限流器:

memLimit := zlimiter.NewLimiter(zlimiter.LimitMemBucket)

创建分布式bucket限流器:

redisLimit := zlimiter.NewLimiter(zlimiter.LimitRedisBucket, rds.RedisInfo{Address: "127.0.0.1:6379", Passwd: "test"})

Token限流

zlimiter中创建集中式、分布式token限流器代码如下:

创建集中式token限流器:

memLimit := zlimiter.NewLimiter(zlimiter.LimitMemToken)

创建分布式token限流器:

redisLimit := zlimiter.NewLimiter(zlimiter.LimitRedisToken, rds.RedisInfo{Address: "127.0.0.1:6379", Passwd: "test"})

zlimiter支持的web框架

- beego

- echo

- gin

- http

其他

部分图片来源于:

https://www.infoq.cn/article/Qg2tX8fyw5Vt-f3HH673