下面的程式碼提供方法,使得在多個時間粒度上都能夠實現速率限制。這樣可以為你的API或其他資源設置多層的保護機制,以避免被過度使用或遭受攻擊。
複雜的速率限制:這段程式的目的是為了實現複雜的速率限制,即在不同的時間粒度上設置不同的速率限制(例如:每秒、每分鐘)。
APIConnection:這是一個模擬的API連接。這個連接有兩個方法:ReadFile
和 ResolveAddress
,兩者在被調用前都會進行速率限制的檢查。
速率限制策略:
MultiLimiter
中,該限制器會對多個限制器進行排序和管理。RateLimiter 接口:定義了限制器需要實現的方法,包括 Wait
和 Limit
。其中,Wait
用於阻止或延遲請求,以確保不超過設定的速率;Limit
返回當前的限制速率。
multiLimiter 結構:一個自定義的多限制器,它包含了一個限制器的切片。在這個限制器中,所有的請求都必須通過每個子限制器的檢查,且按照速率從慢到快的順序進行檢查。
package main
import (
"context"
"golang.org/x/time/rate"
"log"
"os"
"sort"
"sync"
"time"
)
func main() {
defer log.Printf("Done.")
log.SetOutput(os.Stdout)
log.SetFlags(log.Ltime | log.LUTC)
apiConnection := Open()
var wg sync.WaitGroup
wg.Add(20)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
err := apiConnection.ReadFile(context.Background())
if err != nil {
log.Printf("cannot ReadFile: %v", err)
}
log.Printf("ReadFile")
}()
}
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
err := apiConnection.ResolveAddress(context.Background())
if err != nil {
log.Printf("cannot ResolveAddress: %v", err)
}
log.Printf("ResolveAddress")
}()
}
wg.Wait()
}
func Per(eventCount int, duration time.Duration) rate.Limit {
return rate.Every(duration / time.Duration(eventCount))
}
func Open() *APIConnection {
secondLimit := rate.NewLimiter(Per(2, time.Second), 1) // <1>
minuteLimit := rate.NewLimiter(Per(10, time.Minute), 10) // <2>
return &APIConnection{
rateLimiter: MultiLimiter(secondLimit, minuteLimit), // <3>
}
}
type APIConnection struct {
rateLimiter RateLimiter
}
func (a *APIConnection) ReadFile(ctx context.Context) error {
if err := a.rateLimiter.Wait(ctx); err != nil {
return err
}
// Pretend we do work here
return nil
}
func (a *APIConnection) ResolveAddress(ctx context.Context) error {
if err := a.rateLimiter.Wait(ctx); err != nil {
return err
}
// Pretend we do work here
return nil
}
type RateLimiter interface { // <1>
Wait(context.Context) error
Limit() rate.Limit
}
func MultiLimiter(limiters ...RateLimiter) *multiLimiter {
byLimit := func(i, j int) bool {
return limiters[i].Limit() < limiters[j].Limit()
}
sort.Slice(limiters, byLimit) // <2>
return &multiLimiter{limiters: limiters}
}
type multiLimiter struct {
limiters []RateLimiter
}
func (l *multiLimiter) Wait(ctx context.Context) error {
for _, l := range l.limiters {
if err := l.Wait(ctx); err != nil {
return err
}
}
return nil
}
func (l *multiLimiter) Limit() rate.Limit {
return l.limiters[0].Limit() // <3>
}