Может ли кто-нибудь объяснить этот блок кода Go, который использует каналы? Я не понимаю, как он делает 500 действий одновременно - PullRequest
0 голосов
/ 31 октября 2018

Я искал знания о том, как эффективно выполнять множество HTTP-запросов, и натолкнулся на ответ: https://stackoverflow.com/a/23319730/749851 с этим кодом:

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)
}

Я иду с узла, поэтому не совсем понимаю этот код "go".

Какая часть этого ограничивает до 500 HTTP-операций одновременно? И работает ли он порциями по 500, дожидаясь, пока закончится этот порцией 500, затем начинает новые 500, ИЛИ или он просто пыхтит, добавляя еще 1, когда достигает 499 и т. Д.

Я вижу, что функция «workerPool» проходит цикл только столько раз, сколько максимальное количество одновременных запросов, вызывая «работника» 500 раз, но как в конечном итоге это делает следующие 500 или даже весь 1 миллион?

1 Ответ

0 голосов
/ 01 ноября 2018

Это не 500, это 200, и волшебная линия:

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

макс по умолчанию 200; и может быть перегружен переключателем командной строки. Каждая из этих «подпрограмм go», похожих на нелепо легкие потоки, инициализирует себя, а затем ожидает ввода канала. Вот где происходит волшебство - когда приходит запрос, он приводит к отправке по каналу. Существует максимум (200) подпрограмм приема от этого канала, и канал не буферизован, поэтому может быть передано до 200 запросов. 201-ое число заставит отправителя ждать, пока один из рабочих не завершит работу и не вызовет опцию получения (<-). </p>

Тонкость передачи сообщений в Go заслуживает гораздо лучшего, и немного поискать в ней откроет хорошо написанные эссе, учебные пособия и примеры параллелизма в Go.

Удачи с Go; Я думаю, что это замечательный язык. Это элегантно, выразительно и лаконично. Возможно, вам никогда не удастся переварить с ++ или java снова ...

...