parent
4859ea6ee5
commit
f083d52e17
@ -1 +1 @@
|
||||
Subproject commit c0f8a7ef6ddd335b697347dce56271c3d3d8c215
|
||||
Subproject commit 41f585a6f8c8f99ed4b2e279555d6b4dcdf957bc
|
@ -0,0 +1,39 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"golang.org/x/time/rate"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var globalTPSLimiter = NewTPSLimiter()
|
||||
|
||||
type TPSLimiter interface {
|
||||
Limit(ctx context.Context, token string, tps float64, burst int)
|
||||
}
|
||||
|
||||
func NewTPSLimiter() TPSLimiter {
|
||||
return &multipleBucketLimiter{
|
||||
buckets: make(map[string]*rate.Limiter),
|
||||
}
|
||||
}
|
||||
|
||||
// multipleBucketLimiter implements TPSLimiter with multiple bucket support.
|
||||
type multipleBucketLimiter struct {
|
||||
mu sync.Mutex
|
||||
buckets map[string]*rate.Limiter
|
||||
}
|
||||
|
||||
// Limit finds the given bucket, if bucket not exist or limit is changed,
|
||||
// a new bucket will be generated.
|
||||
func (m *multipleBucketLimiter) Limit(ctx context.Context, token string, tps float64, burst int) {
|
||||
m.mu.Lock()
|
||||
bucket, ok := m.buckets[token]
|
||||
if !ok || float64(bucket.Limit()) != tps || bucket.Burst() != burst {
|
||||
bucket = rate.NewLimiter(rate.Limit(tps), burst)
|
||||
m.buckets[token] = bucket
|
||||
}
|
||||
m.mu.Unlock()
|
||||
|
||||
bucket.Wait(ctx)
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLimit(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
l := NewTPSLimiter()
|
||||
finished := make(chan struct{})
|
||||
go func() {
|
||||
l.Limit(context.Background(), "token", 1, 1)
|
||||
close(finished)
|
||||
}()
|
||||
select {
|
||||
case <-finished:
|
||||
case <-time.After(10 * time.Second):
|
||||
a.Fail("Limit should be finished instantly.")
|
||||
}
|
||||
|
||||
finished = make(chan struct{})
|
||||
go func() {
|
||||
l.Limit(context.Background(), "token", 1, 1)
|
||||
close(finished)
|
||||
}()
|
||||
select {
|
||||
case <-finished:
|
||||
case <-time.After(2 * time.Second):
|
||||
a.Fail("Limit should be finished in 1 second.")
|
||||
}
|
||||
|
||||
finished = make(chan struct{})
|
||||
go func() {
|
||||
l.Limit(context.Background(), "token", 10, 1)
|
||||
close(finished)
|
||||
}()
|
||||
select {
|
||||
case <-finished:
|
||||
case <-time.After(1 * time.Second):
|
||||
a.Fail("Limit should be finished instantly.")
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in new issue