为了系统能够稳定的运行,我们常常会对请求进行限流,如果不进行限流的话,海量的请求很可能会把系统给压垮,丢弃部分请求来保证大部分的请求能正常响应往往是一个不错的选择
最常使用的限流方式就是令牌桶了,它的实现简单,可靠性高,只需要按照恒定的速率往令牌桶里面添加令牌,请求过来之后去令牌桶里面获取令牌,如果令牌为空则拒绝访问。
但是令牌桶比较难以使用,因为你很难确定流量限制为多少比较合适,太大的话系统承受不住,太小的话硬件性能得不到充分发挥,并且每台机器的硬件可能都不一致,你没办法把一套令牌桶参数放在不同的服务器上。即使你找到了合适的参数,但随着你系统版本的迭代更新,可能原先的参数就不是那么的合适了。找到一个合适的参数,往往是非常困难的。
我们可以用SmartRateLimiter来解决这个问题,它能根据你的硬件情况,自己找到一个合适的参数。
它会启动一个协程去监控硬件的CPU和内存的使用情况,如果CPU和内存的使用率超过警戒值,那么他会降低令牌的生成速率,减少进入系统的流量。如果CPU和内存的使用率过低并且真实请求速率大于令牌生成速率,则认为系统的工作是不饱和的,这时会加大令牌的生成速率,增加进入系统的流量,通过这种方式,能够根据机器的硬件情况,找到最合适的参数值。
ok,废话不多说,直接上代码
先看数据结构
type TokenBucket struct {
Capacity int64 //令牌桶容量 可以缓存部分令牌
Ch chan bool //令牌桶
CountPerSecond int64 //每秒生成令牌数
AddTimer *time.Ticker //增加令牌的定时器
RealPerSecond int64 //实际上每秒的请求量
Counter int64 //计数器
RealPerSenconds *Queue //缓存每秒请求量的队列
ResetCounterTimer *time.Ticker //重置计数器的定时器
UpdateTimer *time.Ticker //更新生成令牌速率的定时器
CpuPercent float64 //cpu使用率的预警值
MemPercent float64 //内存使用率的预警值
}
创建一个令牌桶
func NewTokenBucket(capacity int64, countPerSecond int64, cpuPercent float64, memPercent float64) *TokenBucket {
tokenBucket := &TokenBucket{
Capacity: capacity,
Ch: make(chan bool, capacity),
CountPerSecond: countPerSecond,
AddTimer: time.NewTicker(time.Duration(int64(time.Second) / countPerSecond)),
RealPerSenconds: &Queue{