Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go例程:进行并发API请求_Go_Concurrency_Channel_Goroutine - Fatal编程技术网

Go例程:进行并发API请求

Go例程:进行并发API请求,go,concurrency,channel,goroutine,Go,Concurrency,Channel,Goroutine,我试图理解通道和goroutine,并试图编写一个goroutine来向服务器发出并发API请求 但是,当我使用goroutine运行代码时,它似乎占用了与没有goroutine时相同的时间 func sendUser(user string, ch chan<- string) { resp,err := http.get("URL"/user) //do the processing and get resp=string ch <- resp }

我试图理解通道和goroutine,并试图编写一个goroutine来向服务器发出并发API请求

但是,当我使用goroutine运行代码时,它似乎占用了与没有goroutine时相同的时间

func sendUser(user string, ch chan<- string)  {
    resp,err := http.get("URL"/user)
    //do the processing and get resp=string
    ch <- resp
}


func AsyncHTTP(users []string) ([]string, error) {
    ch := make(chan string)
    var responses []string
    var user string

    for _ , user = range users {
        go sendUser(user, ch)

        for {
            select {
            case r := <-ch:
                if r.err != nil {
                    fmt.Println(r.err)
                }
                responses = append(responses, r)
                **//Is there a better way to show that the processing of response is complete**?
                if len(responses) == len(users) { 
                    return responses, nil
                }
            case <-time.After(50 * time.Millisecond):
                fmt.Printf(".")
            }
        }
    }
    return responses, nil
}
有没有更好的方法来显示响应处理已完成,并告诉ch不要再等待

  • 什么是等等,同步组?我如何在我的goroutine中使用它


  • 我可能会做这样的事

    func sendUser(user string, ch chan<- string, wg *sync.WaitGroup) {
        defer wg.Done()
        resp, err := http.Get("URL/" + user)
        if err != nil {
            log.Println("err handle it")
        }
        defer resp.Body.Close()
        b, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Println("err handle it")
        }
        ch <- string(b)
    }
    
    func AsyncHTTP(users []string) ([]string, error) {
        ch := make(chan string)
        var responses []string
        var user string
        var wg sync.WaitGroup
        for _, user = range users {
            wg.Add(1)
            go sendUser(user, ch, &wg)
        }
    
        // close the channel in the background
        go func() {
            wg.Wait()
            close(ch)
        }()
        // read from channel as they come in until its closed
        for res := range ch {
            responses = append(responses, res)
        }
    
        return responses, nil
    }
    

    func sendUser(user string,ch chan对于有界并行性/速率限制,我们可以查看

    基本上,这些步骤是:

  • 用于调用API的流输入/params/args到输入通道
  • 运行
    N
    worker goroutines,每个都使用相同(共享)的输入通道。从输入通道获取参数,调用API,将结果发送到结果通道
  • 使用结果通道,如果出现错误,请提前返回
  • sync.WaitGroup
    用于等待所有工作进程goroutine完成(在输入通道耗尽后)

    下面是它的代码示例(您可以立即运行它,尝试将
    NUM\u PARALLEL
    更改为不同的并行数)。将
    BASE\u URL
    更改为您的基本URL

    主程序包
    进口(
    “fmt”
    “io”
    “net/http”
    “strconv”
    “同步”
    “时间”
    )
    //占位符url。将其更改为基本url。
    const BASE_URL=”https://jsonplaceholder.typicode.com/posts/"
    //并行数
    const NUM_PARALLEL=20
    //流输入到输入通道
    
    func streamInputs(在使用无缓冲通道时完成。使用
    Make(chan字符串,10)生成chan)
    或要缓冲的数量。事实上,这不是问题。在循环中,您正在调用
    go sendUser
    ,但随后立即在循环中等待它。
    for
    首先循环所有
    go sendUser
    调用,然后在那里再次循环其余逻辑。@RayfenWindspear,但这不会导致d吗eadlocks,因为通道只有1个缓冲区,如果我将“选择”循环外的语句,那么一个缓冲区将如何保存来自请求的所有响应?这就是它的优点,通道不需要保存任何响应。一个未缓冲的通道基本上不保存任何内容,它直接将其传递给等待接收它的例程。你不在乎是否所有100个或1000个goroutine都在
    ch@RayfenWind上阻塞spear“或者如果你愿意,你可以使用更多的goroutine来更快地使用响应”。你能再解释一下吗?我如何实现它?如果len(响应)==len(用户),有没有更好的方法比这样做更好很好。关于运行并发io还有一条评论。请注意一个请求涉及多少内存。运行数千个goroutine是多么容易,这很容易让人陶醉,但如果每个请求对象最终占用1MB内存,那么就在数千GB的范围内。您可以使用缓冲通道来限制速率。@rayfenwindspear我完全同意,我认为在生产中,我应该使用信号量来限制请求,甚至内存检查。@reticentroot@RayfenWindspear谢谢各位。缓冲通道如何帮助限制速率?如果我理解正确的话,这个u,user=range users{wg.Add(1)循环转到sendUser(user,ch,&wg)     }正在旋转的goroutines数=用户范围。在这种情况下,缓冲有何帮助?我如何才能限制只有正常数量的工作人员旋转并将其返回到通道,然后接管其余的作业?缓冲通道的行为就像一个带队列的桶,如果该桶已满,它将阻塞。例如,如果限制为ten只有10个goroutine将启动,一个项目从存储桶中消耗,另一个通道发送。先进先出。假设您有100个URL。与启动100个goroutine相比,最多只能同时处理10个
    
    func sendUser(user string, ch chan<- string, wg *sync.WaitGroup) {
        defer wg.Done()
        resp, err := http.Get("URL/" + user)
        if err != nil {
            log.Println("err handle it")
        }
        defer resp.Body.Close()
        b, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Println("err handle it")
        }
        ch <- string(b)
    }
    
    func AsyncHTTP(users []string) ([]string, error) {
        ch := make(chan string)
        var responses []string
        var user string
        var wg sync.WaitGroup
        for _, user = range users {
            wg.Add(1)
            go sendUser(user, ch, &wg)
        }
    
        // close the channel in the background
        go func() {
            wg.Wait()
            close(ch)
        }()
        // read from channel as they come in until its closed
        for res := range ch {
            responses = append(responses, res)
        }
    
        return responses, nil
    }