-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathratelimiter.go
77 lines (63 loc) · 1.59 KB
/
ratelimiter.go
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
package ratelimiter
import (
"sync"
"time"
"github.com/sirupsen/logrus"
)
var DefaultWaitCallback = func(waitDuration time.Duration) {
logrus.Debugf("ratelimiter: sleeping %s", waitDuration.String())
time.Sleep(waitDuration)
}
type RateLimiter struct {
max int
per time.Duration
lock *sync.Mutex
count int
lastPeriod time.Time
callback func(waitDuration time.Duration)
}
func New(maxPerMinute int) *RateLimiter {
return NewWithPer(maxPerMinute, time.Minute)
}
func NewWithPer(max int, per time.Duration) *RateLimiter {
return NewWithPerAndCallback(max, per, DefaultWaitCallback)
}
func NewWithPerAndCallback(max int, per time.Duration, callback func(waitDuration time.Duration)) *RateLimiter {
return &RateLimiter{
max: max,
per: per,
lock: &sync.Mutex{},
callback: callback,
}
}
func (r *RateLimiter) Aquire() {
r.AquireWithCount(1)
}
func (r *RateLimiter) AquireWithCount(increment int) {
r.lock.Lock()
currentTime := time.Now()
// this will be the first request
if r.lastPeriod.IsZero() {
r.lastPeriod = currentTime
r.count = 1
r.lock.Unlock()
return
}
// the per is up so the timer can be reset
if currentTime.Truncate(r.per).After(r.lastPeriod.Truncate(r.per)) {
r.lastPeriod = currentTime
r.count = 1
r.lock.Unlock()
return
}
// this request is the last that can be done this per, sleep until the per is up
if r.count == r.max {
nextPeriod := currentTime.Add(r.per).Truncate(r.per)
waitDuration := nextPeriod.Sub(currentTime)
if r.callback != nil {
r.callback(waitDuration)
}
}
r.count++
r.lock.Unlock()
}