-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrateLimiter.go
88 lines (74 loc) · 1.61 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
78
79
80
81
82
83
84
85
86
87
88
package main
import (
"log"
"strconv"
"strings"
"sync"
"time"
)
type RateLimiter struct {
tokens uint64
maxTokens uint64
tokenRate time.Duration
mu sync.Mutex
cond *sync.Cond
stopCh chan struct{}
}
func NewRateLimiter(rateLimit string) *RateLimiter {
parts := strings.Split(rateLimit, "/")
if len(parts) != 2 {
log.Panic("invalid rate limit. Must be in the format of <limit>/<time>. Ex. 20/m 30/s 300/h")
}
limit, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
log.Panic("invalid rate limit. Must be in the format of <limit>/<time>. Ex. 20/m 30/s 300/h")
}
var duration time.Duration
switch parts[1] {
case "s":
duration = time.Second
case "m":
duration = time.Minute
case "h":
duration = time.Hour
default:
log.Panic("invalid rate limit. Must be in the format of <limit>/<time>. Ex. 20/m 30/s 300/h")
}
rl := &RateLimiter{
tokens: limit,
maxTokens: limit,
tokenRate: duration / time.Duration(limit),
stopCh: make(chan struct{}),
}
rl.cond = sync.NewCond(&rl.mu)
go rl.refillTokens()
return rl
}
func (rl *RateLimiter) refillTokens() {
ticker := time.NewTicker(rl.tokenRate)
defer ticker.Stop()
for {
select {
case <-ticker.C:
rl.mu.Lock()
if rl.tokens < rl.maxTokens {
rl.tokens++
rl.cond.Signal() // Notify a waiting goroutine that a token is available
}
rl.mu.Unlock()
case <-rl.stopCh:
return
}
}
}
func (rl *RateLimiter) Acquire() {
rl.mu.Lock()
defer rl.mu.Unlock()
for rl.tokens == 0 {
rl.cond.Wait() // Wait until a token is available
}
rl.tokens--
}
func (rl *RateLimiter) Stop() {
close(rl.stopCh)
}