-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrate_limit.go
98 lines (85 loc) · 2.02 KB
/
rate_limit.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
89
90
91
92
93
94
95
96
97
98
package ratelimit
import (
"fmt"
"sync"
"time"
)
type window struct {
count uint64
timestamp time.Time
}
type SlidingWindow struct {
previous *window
current *window
}
type RateLimitOptions struct {
Limit uint64
timeWindow time.Duration
}
type RateLimiter struct {
sync.RWMutex
maxRequests uint64
windows map[string]*SlidingWindow
options RateLimitOptions
}
func (rl *RateLimiter) SetLimit(limit uint64) {
rl.options.Limit = limit
}
func (rl *RateLimiter) SetTimeWindow(timeWindow time.Duration) {
rl.options.timeWindow = timeWindow
}
func NewRateLimiter(opts ...Options) Limiter {
rlOpts := &RateLimitOptions{
Limit: 20,
timeWindow: 60 * time.Second,
}
for _, opt := range opts {
opt(rlOpts)
}
rl := &RateLimiter{
windows: make(map[string]*SlidingWindow),
options: *rlOpts,
}
return rl
}
func (r *RateLimiter) Allow(key string) bool {
r.Lock()
defer r.Unlock()
now := time.Now()
slwindow, exists := r.windows[key]
if !exists {
slwindow = &SlidingWindow{
previous: &window{count: 0, timestamp: now},
current: &window{count: 0, timestamp: now},
}
r.windows[key] = slwindow
}
// If current window expires the current window becomes previous and new current window is initiated.
timeElapsed := now.Sub(slwindow.current.timestamp)
if timeElapsed >= r.options.timeWindow {
slwindow.previous = slwindow.current
slwindow.current = &window{count: 0, timestamp: now}
}
weight := 1 - (float64(timeElapsed) / float64(r.options.timeWindow))
fmt.Println("Weight:", weight)
if weight < 0 {
weight = 0
}
// Calculate total count including weighted previous window
weightedPreviousCount := uint64(float64(slwindow.previous.count) * weight)
totalCount := weightedPreviousCount + slwindow.current.count
if totalCount >= r.options.Limit {
return false
}
slwindow.current.count++
return true
}
func (r *RateLimiter) GetCurrentCount(key string) uint64 {
r.RLock()
defer r.RUnlock()
slwindow, exists := r.windows[key]
if !exists {
return 0
}
return slwindow.current.count
}