无法抓住我的头周围去率包
Unable to grasp my head around go rate package
我需要将请求速率限制为 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 requests/second 吗?
首先,您有一场数据竞赛。多个 goroutine 写入 successful
没有同步:未定义的行为。
您可以使用 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 sleep,那么所有都将被允许,输出将是 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 events/sec:
limiter := rate.NewLimiter(rate.Limit(5), 5)
不休眠地使用这个limiter
,你也会得到5
的输出。这是因为在不达到速率限制的情况下允许 5 个事件,如果没有睡眠,则不允许其余事件。
我需要将请求速率限制为 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 requests/second 吗?
首先,您有一场数据竞赛。多个 goroutine 写入 successful
没有同步:未定义的行为。
您可以使用 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 sleep,那么所有都将被允许,输出将是 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 events/sec:
limiter := rate.NewLimiter(rate.Limit(5), 5)
不休眠地使用这个limiter
,你也会得到5
的输出。这是因为在不达到速率限制的情况下允许 5 个事件,如果没有睡眠,则不允许其余事件。