问题

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "runtime"
)

func main() {
    num := 6
    for index := 0; index < num; index++ {
        resp, _ := http.Get("https://www.baidu.com")
        _, _ = ioutil.ReadAll(resp.Body)
    }
    fmt.Printf("此时goroutine个数= %d\n", runtime.NumGoroutine())
}
resp.Body.Close()goroutine

怎么答

resp.Body.Close()goroutinemain函数goroutine

解释

golanghttp
http.Get()

-- DefaultClient.Get
----func (c *Client) do(req *Request)
------func send(ireq *Request, rt RoundTripper, deadline time.Time)
-------- resp, didTimeout, err = send(req, c.transport(), deadline) 
// 以上代码在 go/1.12.7/libexec/src/net/http/client:174 

func (c *Client) transport() RoundTripper {
    if c.Transport != nil {
        return c.Transport
    }
    return DefaultTransport
}
http.GetDefaultTransport
DefaultTransport
// It establishes network connections as needed
// and caches them for reuse by subsequent calls.
DefaultTransport
DefaultTransport

接着上面的代码堆栈往下翻

func send(ireq *Request, rt RoundTripper, deadline time.Time) 
--resp, err = rt.RoundTrip(req) // 以上代码在 go/1.12.7/libexec/src/net/http/client:250
func (t *Transport) RoundTrip(req *http.Request)
func (t *Transport) roundTrip(req *Request)
func (t *Transport) getConn(treq *transportRequest, cm connectMethod)
func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (*persistConn, error) {
    ...
    go pconn.readLoop()  // 启动一个读goroutine
    go pconn.writeLoop() // 启动一个写goroutine
    return pconn, nil
}
读goroutine写goroutinehttp.Get()两个goroutineclose
close
读goroutinereadLoop()
func (pc *persistConn) readLoop() {
    alive := true
    for alive {
        ...
        // Before looping back to the top of this function and peeking on
        // the bufio.Reader, wait for the caller goroutine to finish
        // reading the response body. (or for cancelation or death)
        select {
        case bodyEOF := <-waitForBodyRead:
            pc.t.setReqCanceler(rc.req, nil) // before pc might return to idle pool
            alive = alive &&
                bodyEOF &&
                !pc.sawEOF &&
                pc.wroteRequest() &&
                tryPutIdleConn(trace)
            if bodyEOF {
                eofc <- struct{}{}
            }
        case <-rc.req.Cancel:
            alive = false
            pc.t.CancelRequest(rc.req)
        case <-rc.req.Context().Done():
            alive = false
            pc.t.cancelRequest(rc.req, rc.req.Context().Err())
        case <-pc.closech:
            alive = false
        }
        ...
    }
}
readLoopalivetruegoroutineselectgoroutinebodybodyrequestcancelrequestcontext DonetruepersistConn
bodycase
alive = alive &&
    bodyEOF &&
    !pc.sawEOF &&
    pc.wroteRequest() &&
    tryPutIdleConn(trace)
bodyEOFwaitForBodyReadtruefalsealivealive=true读goroutinegoroutine

那么这个通道的值是从哪里过来的呢?

// go/1.12.7/libexec/src/net/http/transport.go: 1758
        body := &bodyEOFSignal{
            body: resp.Body,
            earlyCloseFn: func() error {
                waitForBodyRead <- false
                <-eofc // will be closed by deferred call at the end of the function
                return nil

            },
            fn: func(err error) error {
                isEOF := err == io.EOF
                waitForBodyRead <- isEOF
                if isEOF {
                    <-eofc // see comment above eofc declaration
                } else if err != nil {
                    if cerr := pc.canceled(); cerr != nil {
                        return cerr
                    }
                }
                return err
            },
        }
earlyCloseFnwaitForBodyReadfalsealivefalsereadLoop()goroutinefnbodyio.EOFcasewaitForBodyReadtruealivetruereadLoop()goroutinetryPutIdleConn(trace)
// tryPutIdleConn adds pconn to the list of idle persistent connections awaiting
// a new request.
// If pconn is no longer needed or not in a good state, tryPutIdleConn returns
// an error explaining why it wasn't registered.
// tryPutIdleConn does not close pconn. Use putOrCloseIdleConn instead for that.
func (t *Transport) tryPutIdleConn(pconn *persistConn) error
tryPutIdleConnpconn
fnearlyCloseFn
func (es *bodyEOFSignal) Close() error {
    es.mu.Lock()
    defer es.mu.Unlock()
    if es.closed {
        return nil
    }
    es.closed = true
    if es.earlyCloseFn != nil && es.rerr != io.EOF {
        return es.earlyCloseFn() // 关闭时执行 earlyCloseFn
    }
    err := es.body.Close()
    return es.condfn(err)
}
resp.Body.Close()earlyCloseFnreadLoop()waitForBodyReadfalsealivefalsereadLoop()goroutinegoroutine
b, err = ioutil.ReadAll(resp.Body)
--func ReadAll(r io.Reader) 
----func readAll(r io.Reader, capacity int64) 
------func (b *Buffer) ReadFrom(r io.Reader)


// go/1.12.7/libexec/src/bytes/buffer.go:207
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
    for {
        ...
        m, e := r.Read(b.buf[i:cap(b.buf)])  // 看这里,是body在执行read方法
        ...
    }
}
readbodyEOFSignal
func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
    ...
    n, err = es.body.Read(p)
    if err != nil {
        ... 
    // 这里会有一个io.EOF的报错,意思是读完了
        err = es.condfn(err)
    }
    return
}


func (es *bodyEOFSignal) condfn(err error) error {
    if es.fn == nil {
        return err
    }
    err = es.fn(err)  // 这了执行了 fn
    es.fn = nil
    return err
}
bodyioutil.ReadAll()bodyfnreadLoop()waitForBodyReadtruealivetruereadLoop()goroutinegoroutinetryPutIdleConn(trace)

总结

6Body.Close()ioutil.ReadAll()读goroutine写goroutinemain goroutine3个goroutineioutil.ReadAll()resp.Body.Close()读goroutine写goroutine