Repository navigation
Expand file tree
/
Copy pathsliding_window.go
More file actions
90 lines (76 loc) · 1.93 KB
/
Copy pathsliding_window.go
File metadata and controls
90 lines (76 loc) · 1.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package limiter
// 滑动时间窗口算法
import (
"sync"
"time"
)
type timeSlot struct {
begin time.Time // 此time slot的时间起点
count int // 落在此time slot内的请求数
}
type SlidingWindowLimiter struct {
slotDuration time.Duration // time slot的长度
winDuration time.Duration // sliding window的长度
maxReq int // 大窗口时间内允许的最大请求数
windows []*timeSlot // window内slot的合集
mu sync.Mutex // 互斥锁保护其他字段
}
func NewSlidingWindowLimiter(slotDuration, winDuration time.Duration, maxReq int) *SlidingWindowLimiter {
return &SlidingWindowLimiter{
slotDuration: slotDuration,
winDuration: winDuration,
maxReq: maxReq,
}
}
func (l *SlidingWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
now := time.Now()
timeoutOffset := -1
// 将已经过期的time slot移出时间窗口
for i, ts := range l.windows {
if ts.begin.Add(l.winDuration).After(now) {
break
}
timeoutOffset = i
}
if timeoutOffset > -1 {
l.windows = l.windows[timeoutOffset+1:]
}
// 判断请求是否超限
var ok bool
if l.countReq() < l.maxReq {
ok = true
}
// 记录这次的请求数
var lastSlot *timeSlot
if len(l.windows) > 0 {
lastSlot = l.windows[len(l.windows)-1]
// 如果当前时间已经超过这个时间插槽的跨度,那么新建一个时间插槽
if lastSlot.begin.Add(l.slotDuration).Before(now) {
lastSlot = &timeSlot{begin: now, count: 1}
l.windows = append(l.windows, lastSlot)
} else {
lastSlot.count++
}
} else {
lastSlot = &timeSlot{begin: now, count: 1}
l.windows = append(l.windows, lastSlot)
}
return ok
}
func (l *SlidingWindowLimiter) countReq() int {
var count int
for _, ts := range l.windows {
count += ts.count
}
return count
}
func (l *SlidingWindowLimiter) Run(fn func()) error {
if !l.Allow() {
return ErrFreqExceed
}
// Invoke fn
fn()
return nil
}