有人可以解释这个使用通道的 Go 代码块吗?我不明白它是如何一次执行 500 个动作的

Could someone explain this block of Go code that uses channels? I don't understand how it's doing 500 actions at a time

我正在查找有关如何有效执行大量 HTTP 请求的知识,然后我遇到了这个答案: 使用以下代码:

package main

import (
    "flag"
    "fmt"
    "log"
    "net/http"
    "runtime"
    "time"
)

var (
    reqs int
    max  int
)

func init() {
    flag.IntVar(&reqs, "reqs", 1000000, "Total requests")
    flag.IntVar(&max, "concurrent", 200, "Maximum concurrent requests")
}

type Response struct {
    *http.Response
    err error
}

// Dispatcher
func dispatcher(reqChan chan *http.Request) {
    defer close(reqChan)
    for i := 0; i < reqs; i++ {
        req, err := http.NewRequest("GET", "http://localhost/", nil)
        if err != nil {
            log.Println(err)
        }
        reqChan <- req
    }
}

// Worker Pool
func workerPool(reqChan chan *http.Request, respChan chan Response) {
    t := &http.Transport{}
    for i := 0; i < max; i++ {
        go worker(t, reqChan, respChan)
    }
}

// Worker
func worker(t *http.Transport, reqChan chan *http.Request, respChan chan Response) {
    for req := range reqChan {
        resp, err := t.RoundTrip(req)
        r := Response{resp, err}
        respChan <- r
    }
}

// Consumer
func consumer(respChan chan Response) (int64, int64) {
    var (
        conns int64
        size  int64
    )
    for conns < int64(reqs) {
        select {
        case r, ok := <-respChan:
            if ok {
                if r.err != nil {
                    log.Println(r.err)
                } else {
                    size += r.ContentLength
                    if err := r.Body.Close(); err != nil {
                        log.Println(r.err)
                    }
                }
                conns++
            }
        }
    }
    return conns, size
}

func main() {
    flag.Parse()
    runtime.GOMAXPROCS(runtime.NumCPU())
    reqChan := make(chan *http.Request)
    respChan := make(chan Response)
    start := time.Now()
    go dispatcher(reqChan)
    go workerPool(reqChan, respChan)
    conns, size := consumer(respChan)
    took := time.Since(start)
    ns := took.Nanoseconds()
    av := ns / conns
    average, err := time.ParseDuration(fmt.Sprintf("%d", av) + "ns")
    if err != nil {
        log.Println(err)
    }
    fmt.Printf("Connections:\t%d\nConcurrent:\t%d\nTotal size:\t%d bytes\nTotal time:\t%s\nAverage time:\t%s\n", conns, max, size, took, average)
}

我来自 node,所以我不太理解这段 "go" 代码。

它的哪一部分将它限制为一次 500 个 HTTP 操作?它是以 500 块为单位运行,等到 500 块完成然后开始一个新的 500,还是它总是在达到 499 时再添加 1,等等

我看到 "workerPool" func 循环次数仅与最大并发请求数一样多,调用 "worker" 500 次,但接下来的 500 次甚至它是如何进行的最终整整一百万?

不是500,是200,魔法线是:

for i := 0; i < max; i++ {
    go worker(t, reqChan, respChan)
}

最大值默认为200;并且可能会被命令行开关淹没。 这些类似于可笑的轻量级线程的“go routines”中的每一个都会初始化自身,然后等待通道输入。这就是魔法发生的地方——当一个请求进来时,它会导致在一个通道上发送。从该通道接收最多 (200) 个 go-routines,并且该通道是无缓冲的,因此最多可以处理 200 个请求。第 201 个将导致发送方等待,直到其中一名工作人员完成并调用接收 (<-) 操作。

Go 消息传递的微妙之处值得更好的理解,稍微谷歌一下就会发现写得很好的论文、教程和 Go 并发示例。

祝你 Go 好运;我认为这是一种美妙的语言。它优雅、富有表现力且简洁。您可能再也无法忍受 C++ 或 java....