zlimiter

package module
v0.0.0-...-d9f8102 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 14, 2021 License: MIT Imports: 5 Imported by: 0

README

为什么要限流

恶意的流量访问

​ 线上服务运行过程中,或多或少都会接收到恶意的流量访问,巨量的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

Documentation

Index

Constants

View Source
const (
	// LimitMemFixWindow 用于标识基于内存的固定窗口限流
	LimitMemFixWindow = common.LimitMemFixWindow
	// LimitMemSlideWindow 用于标识基于内存的滑动窗口限流
	LimitMemSlideWindow = common.LimitMemSlideWindow
	// LimitMemBucket 用于标识基于内存的bucket限流
	LimitMemBucket = common.LimitMemBucket
	// LimitMemToken 用于标识基于内存的Token限流
	LimitMemToken = common.LimitMemToken
	// LimitRedisFixWindow 用于标识分布式固定窗口限流
	LimitRedisFixWindow = common.LimitRedisFixWindow
	// LimitRedisSlideWindow 用于标识分布式滑动窗口限流
	LimitRedisSlideWindow = common.LimitRedisSlideWindow
	// LimitRedisBucket 用于标识分布式bucket限流
	LimitRedisBucket = common.LimitRedisBucket
	// LimitRedisToken 用于标识分布式token限流
	LimitRedisToken = common.LimitRedisToken

	// ErrorReturnNoLeft 规则已无限额
	ErrorReturnNoLeft = common.ErrorReturnNoLeft
	// ErrorReturnItemNotExist key对应规则不存在
	ErrorReturnItemNotExist = common.ErrorReturnItemNotExist
	// ErrorReturnNoMeans 无意义返回值,用于erro!=nil时
	ErrorReturnNoMeans = common.ErrorReturnNoMeans
	// ErrorReturnBucket Bucket限流返回值 无意义固定值
	ErrorReturnBucket = common.ErrorReturnBucket
)

Variables

This section is empty.

Functions

This section is empty.

Types

type DriverI

type DriverI interface {
	Init(...interface{}) error
	Add(string, int64, time.Duration, ...interface{}) error
	Get(string) (int64, error)
	Set(string, int64, time.Duration, ...interface{}) error
	Del(string) error
}

DriverI zlimiter驱动接口,任何zlimiter的driver都要实现该接口 Add、Get、Set、Del分别为新增、获取、设置、删除的相关处理函数

type Limits

type Limits struct {
	// contains filtered or unexported fields
}

Limits zlimiter实例类,所有功能都是通过该struct实现

func NewLimiter

func NewLimiter(limiterType int64, args ...interface{}) *Limits

NewLimiter 工厂函数,用于创建一个限流器,关于各种限流器的说明请参见:,如果限流器创建过程中

发生错误,则会Log.Fatal错误信息并停止,否则返回相关限流器

Input:

limiterType : 需要创建的限流器的类型,具体包括:
	LimitMemFixWindow	单机固定窗口限流
	LimitMemSlideWindow	单机滑动窗口限流
	LimitMemToken	单机token限流
	LimitMemBucket	单机桶限流
	LimitRedisFixWindow 分布式固定窗口限流
	LimitRedisSlideWindow	分布式滑动窗口限流
	LimitRedisBucket	分布式桶限流
	LimitRedisToken	分布式token限流
args : 初始化参数,主要用于Redis相关的初始化,注意redis初始化需要RedisInfo类型的变量

Output:

*Limits : 成功创建的限流器

func (*Limits) Add

func (l *Limits) Add(key string, limit int64, tmSpan time.Duration, others ...interface{}) error

Add 创建一条基于key的限流规则

Input :
	key : 限流标识,用于唯一标识一条限流规则
	limit : tmSpan时间段内的限流数,与tmSpan同时实现tmSpan时间段内限流limit次语义
	tmSpan : 时间段,与limit同时实现tmSpan时间段内限流limit次语义
	others : 其他相关参数,目前bucket和token限流的max值
Output :
	error : 成功为nil,否则为具体错误信息

func (*Limits) Del

func (l *Limits) Del(key string) error

Del 删除基于key的限流

Input :
	key : 要删除的限流的标识
Output :
	error : 相关错误信息,成功为nil 否则为相关错误

func (*Limits) Get

func (l *Limits) Get(key string) (int64, error)

Get 获取基于key的请求的相关信息,包括:本次访问是否可以放行,剩余可放行的访问数

Input :
	key : 限流标识,对应于限流规则
Output :
	bool : 访问是否可以放行,当剩余可访问次数<0时候,为true,否则为false。注意,基于bucket的访问
		本参数永远为false
	int64 : 剩余的可访问次数, 注意,基于bucket本参数永远为-1
	error : 相关错误信息,成功为nil 否则为相关错误

func (*Limits) Set

func (l *Limits) Set(key string, limits int64, tmDuration time.Duration, others ...interface{}) error

Set 创建或者重置基于key的限流规则

Input :
	key : 限流标识,用于唯一标识一条限流规则
	limit : tmSpan时间段内的限流数,与tmSpan同时实现tmSpan时间段内限流limit次语义
	tmSpan : 时间段,与limit同时实现tmSpan时间段内限流limit次语义
	others : 其他相关参数,目前bucket和token限流的max值
Output :
	error : 成功为nil,否则为具体错误信息

Directories

Path Synopsis
driver
example
gin

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL