无法理解go rate包的含义。

huangapple go评论78阅读模式
英文:

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个请求吗?

英文:

I need to rate limit the requests to an API and I'm considering using the native golang.org/x/time/rate package for that purpose. In order to fiddling a little bit with its API and make sure that my assumptions are correct, I've created this tests, but it definitely seems that I'm missing something here:

package main

import (
    &quot;github.com/stretchr/testify/require&quot;
    &quot;golang.org/x/time/rate&quot;
    &quot;sync&quot;
    &quot;testing&quot;
)

func TestLimiter(t *testing.T) {
    limiter := rate.NewLimiter(rate.Limit(5),1)
    wg := sync.WaitGroup{}
    successful := 0

    for i:=1; i&lt;=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
}

What is the explanation for why this is? Shouldn't the rate limiter allow 5 requests/second?

答案1

得分: 4

首先,你有一个数据竞争。多个 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 毫秒的睡眠,那么所有事件都将被允许,并且输出将为 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 个事件,而没有睡眠,其余的事件将不被允许。

英文:

First, you have a data race. Multiple goroutines write successful without synchronization: undefined behavior.

You may use the sync/atomic package for an easy and safe counting:

limiter := rate.NewLimiter(rate.Limit(5), 1)
wg := sync.WaitGroup{}
successful := int32(0)

for i := 1; i &lt;= 10; i++ {
	wg.Add(1)
	go func() {
		defer wg.Done()
		if limiter.Allow() {
			atomic.AddInt32(&amp;successful, 1)
		}
	}()
}
wg.Wait()

fmt.Println(successful)

This will output:

1

Why? Because you allow 5 events per second, that's 1 event per 0.2 seconds. Launching 10 goroutines and checking will take less than 0.2 seconds, so only one event will be allowed.

If you add 200 ms sleep in the loop, then all will be allowed and output will be 10:

for i := 1; i &lt;= 10; i++ {
	time.Sleep(200 * time.Millisecond)
	wg.Add(1)
	go func() {
		defer wg.Done()
		if limiter.Allow() {
			atomic.AddInt32(&amp;successful, 1)
		}
	}()
}

If you add 100 ms sleep, then on average half of them will be allowed and output will be 5.

What you want probably is allow a burst of 5 with 5 events/sec:

limiter := rate.NewLimiter(rate.Limit(5), 5)

Using this limiter without sleep, you'll also get an output of 5. That's because 5 events are allowed without hitting the rate limit, and without sleep the rest will not be allowed.

huangapple
  • 本文由 发表于 2021年12月20日 15:08:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/70418427.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定