-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathratelimit.go
101 lines (86 loc) · 2.99 KB
/
ratelimit.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
99
100
101
package gohttpclient
import (
"fmt"
"net/http"
"net/url"
"strings"
"sync"
"go.uber.org/ratelimit"
)
// RateLimitConstructor defines the constructor of a rate limiter.
type RateLimitConstructor func() ratelimit.Limiter
// RateLimitFunc enforces the rate limit.
type RateLimitFunc func(req *http.Request, option RateLimitOption) error
// defaultRateLimitFunc gets a request token, and if no token is currently available, it waits.
var defaultRateLimitFunc RateLimitFunc = func(req *http.Request, option RateLimitOption) error {
key := ""
if req != nil && req.URL != nil {
key = fmt.Sprintf("%s %s", req.Method, strings.ToLower(getURLStringEndWithPath(req.URL)))
}
val, _ := option.RateLimits.LoadOrStore(key, option.RateLimitConstructor())
rl := val.(ratelimit.Limiter)
_ = rl.Take()
return nil
}
// RateLimitAllRequestsFunc enforces a rate limit, each request is included in the rate limit,
// and it does not distinguish the domain name of the request.
var RateLimitAllRequestsFunc RateLimitFunc = func(req *http.Request, option RateLimitOption) error {
key := "__all__"
val, _ := option.RateLimits.LoadOrStore(key, option.RateLimitConstructor())
rl := val.(ratelimit.Limiter)
_ = rl.Take()
return nil
}
// RateLimitOption defines a rate limit option configuration.
type RateLimitOption struct {
Rate int
RateLimitConstructor RateLimitConstructor
RateLimits *sync.Map
RateLimitFunc RateLimitFunc
}
func (r RateLimitOption) isEnabled() bool {
return r.RateLimits != nil
}
// NewRateLimitOption creates a rate limit option configuration.
// The parameter rate defines the maximum number of requests per second.
// If it exceeds maximum times, the excess requests will wait until the next second to execute.
// The requested address needs to be specially explained,
// and the parameters after the link question mark will be omitted.
// Different requested addresses have different capacity of maximum times per second.
// Of course, you can also customize the algorithm
// and stipulate that different domain names use different capacity limits.
func NewRateLimitOption(rate int) RateLimitOption {
return RateLimitOption{
Rate: rate,
RateLimitConstructor: func() ratelimit.Limiter {
return ratelimit.New(rate)
},
RateLimits: &sync.Map{},
RateLimitFunc: defaultRateLimitFunc,
}
}
// RateLimitHandler creates a rate-limiting interceptor that limits the maximum number of requests per second.
func RateLimitHandler(option RateLimitOption) RequestHandler {
return func(req *http.Request, handlerFunc RequestHandlerFunc) (resp *http.Response, err error) {
err = option.RateLimitFunc(req, option)
if err != nil {
return
}
return handlerFunc(req)
}
}
func getURLStringEndWithPath(u *url.URL) string {
v := url.URL{
Scheme: u.Scheme,
Opaque: "",
User: nil,
Host: u.Host,
Path: u.Path,
RawPath: u.RawPath,
ForceQuery: u.ForceQuery,
RawQuery: "",
Fragment: "",
RawFragment: "",
}
return v.String()
}