我需要對 API 的請求進行速率限制,我正在考慮golang.org/x/time/rate為此目的使用本機包。為了稍微擺弄一下它的 API 并確保我的假設是正確的,我創建了這個測驗,但似乎我在這里遺漏了一些東西:
package main
import (
"github.com/stretchr/testify/require"
"golang.org/x/time/rate"
"sync"
"testing"
)
func TestLimiter(t *testing.T) {
limiter := rate.NewLimiter(rate.Limit(5),1)
wg := sync.WaitGroup{}
successful := 0
for i:=1; i<=10; i {
wg.Add(1)
go func() {
defer wg.Done()
if limiter.Allow() {
successful
}
}()
}
wg.Wait()
require.Equal(t, 5, successful)
// This test fails with
// Expected :5
// Actual :1
}
有人能解釋一下這是為什么嗎?速率限制器不應該允許 5 req/s 嗎?
uj5u.com熱心網友回復:
首先,你有一個資料競賽。多個 goroutinesuccessful沒有同步寫入:未定義的行為。
您可以使用該sync/atomic軟體包進行簡單安全的計數:
limiter := rate.NewLimiter(rate.Limit(5), 1)
wg := sync.WaitGroup{}
successful := int32(0)
for i := 1; i <= 10; i {
wg.Add(1)
go func() {
defer wg.Done()
if limiter.Allow() {
atomic.AddInt32(&successful, 1)
}
}()
}
wg.Wait()
fmt.Println(successful)
這將輸出:
1
為什么?因為您允許每秒 5 個事件,所以每 0.2 秒 1 個事件。啟動 10 個 goroutine 并檢查將花費不到 0.2 秒的時間,因此只允許一個事件。
如果在回圈中添加 200 ms 睡眠,則所有內容都將被允許,輸出將為10:
for i := 1; i <= 10; i {
time.Sleep(200 * time.Millisecond)
wg.Add(1)
go func() {
defer wg.Done()
if limiter.Allow() {
atomic.AddInt32(&successful, 1)
}
}()
}
如果您添加 100 毫秒的睡眠,則平均將允許其中的一半,并且輸出將為5.
您可能想要的是允許以 5 個事件/秒突發 5 個:
limiter := rate.NewLimiter(rate.Limit(5), 5)
在limiter沒有睡眠的情況下使用它,您還將獲得5. 這是因為在不達到速率限制的情況下允許 5 個事件,而沒有睡眠則不允許其余事件。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/388919.html
上一篇:開發多模塊Go作業區的問題
下一篇:檢測fyne中的阻力
