go-rate
https://pkg.go.dev/github.com/beefsack/go-rate#section-sourcefiles
用法
new()
:创建一个实例wait()
:用来阻塞等待try()
:返回两个值,一个是表示是否可以立刻执行,另一个是需要延迟多久执行
实现
它的实现是,任何一段时间interval内,不能有超过limit个请求
维护一个队列,队列头是最早的时间,队列元素不会删(相当于固定的token数),到了limit后就从头部移动到尾部
假如当前队列中数目不超过limit个(最一开始的情况)
后面的情况就是:每次对比下队列中最早的时间和当前时间,最早时间超过了当前时间说明已经被处理过了相当于有空闲token了,所以可以处理(设置时间,移动到队列尾),不用sleep;否则就sleep这个差值。
// Try returns true if under the rate limit, or false if over and the
// remaining time before the rate limit expires.
func (r *RateLimiter) Try() (ok bool, remaining time.Duration) {
r.mtx.Lock()
defer r.mtx.Unlock()
now := time.Now()
if l := r.times.Len(); l < r.limit {
r.times.PushBack(now)
return true, 0
}
frnt := r.times.Front()
if diff := now.Sub(frnt.Value.(time.Time)); diff < r.interval {
return false, r.interval - diff
}
frnt.Value = now
r.times.MoveToBack(frnt)
return true, 0
}
关键的操作是:if diff := now.Sub(frnt.Value.(time.Time)); diff < r.interval
走到这里队列是已经满了,必须挤掉一个人,且是挤掉最老的人,frnt
是队列中最老的时间,所以当前时间和他相比,只要当前时间比最老的时间老且超过的时间小于r.interval - diff。
设计的含义是:r.interval这段时间内只能有r.limit个元素,队列长度最大就是r.limit;队列从头到尾时间是越来越新(大)。所以如果来一个新的请求,队列又满了,那么需要这个新请求的时间比最老的时间大且要大r.interval这么久,因为超过r.interval,这个最老的请求就可以移除了
aa
其他
它的处理不是时间片均匀的, 比如说设置1s有100个token,有100个线程来抢,cpu处理很快的话,可能0.1秒就处理完了剩下0.9秒都是闲的,这样的话CPU峰值会高但其他时间可能很闲。即CPU的工作不是很均匀。
增加分时间段不同的限流的功能
队列长度是动态变化的,不同的时间段,队列长度不同