每秒运行的goroutine数量是否可以进行限制?

4

我有一组URL,需要使用goroutine并发发送HTTP请求。有没有办法检查并限制每秒发送的HTTP请求数量?


4
Go 无法全局限制 goroutine。如果你正在进行 HTTP 请求,你自己可以限制它们吗? - JimB
@JimB:我应该如何限制它们?例如,我希望程序每秒发送少于或等于10个HTTP请求。 - Pig
2
你可以使用信号量来限制最大并发请求,或者使用类似于令牌桶或漏桶的速率限制算法。 - JimB
2个回答

14

使用通道和goroutine来适应漏桶算法的一种非常简单的Go版本。在发出请求之前,从rate通道接收一个令牌将检查速率,并在速率限制器为空时阻塞。

// create a buffered channel.
// The capacity of the channel is maximum burst that can be made.
rate := make(chan struct{}, 10)

go func() {
    ticker := time.NewTicker(100 * time.Millisecond)
    for range ticker.C {
        rate <- struct{}{}
    }
}()

如果一系列请求的时间超过了平均速率,则这些请求将变成并发请求,因此您可能还需要限制并发。您可以添加第二个通道作为信号量,在发出请求之前向信号量添加一个令牌,并在请求完成后将其删除。

// limit concurrency to 5
semaphore := make(chan struct{}, 5)

// in request function
semaphore <- struct{}{}
defer func() {
    <-semaphore
}()

这里有一个稍微更完整的例子:

https://play.golang.org/p/ZrTPLcdeDF


如果您需要更多的控制/功能,可以在golang.org/x/time/rate找到一个令牌桶实现。 - Dave C
这是一个使用golang.org/x/time/rate的“稍微完整一些的示例”:https://play.golang.org/p/44rUKLdTPbO - Dave C
@DaveC 在你的“稍微完整一些的例子”中,为什么要同时使用限制器和信号量?难道限制器不能完成所有的功能吗? - jshen
@jshen(注意:我写这个已经有一段时间了,我认为我大部分是在复制/翻译Jim的示例。)限制器仅限制可以启动新请求的速率,如果请求具有长生命周期(例如,如果“添加令牌”打印被替换为实际工作,例如OP中的HTTP请求),则可以同时运行的数量没有限制。信号量或工作池是限制此类问题的简单方法。通常,我会使用工作池而不是信号量,但哪种更好取决于问题和使用特定情况。 - Dave C
@jshen:使用int意味着int的值具有意义,但实际上它并没有。空的struct{}更明显地只是一个不透明的标记。 - JimB
显示剩余3条评论

-1

这是一个简单的包,可以帮助您限制允许同时运行的goroutine数量。请查看https://github.com/zenthangplus/goccm

示例:

package main

import (
    "fmt"
    "goccm"
    "time"
)

func main()  {
    // Limit 3 goroutines to run concurrently.
    c := goccm.New(3)

    for i := 1; i <= 10; i++ {

        // This function have to call before any goroutine
        c.Wait()

        go func(i int) {
            fmt.Printf("Job %d is running\n", i)
            time.Sleep(2 * time.Second)

            // This function have to when a goroutine has finished
            // Or you can use `defer c.Done()` at the top of goroutine.
            c.Done()
        }(i)
    }

    // This function have to call to ensure all goroutines have finished 
    // after close the main program.
    c.WaitAllDone()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接