用户协程在使用 http.Client 发送请求时,一路到 http.Transport.roundTrip -> http.persistConn.roundTrip:
pc.writech <- writeRequest{req, writeErrCh, continueCh} resc := make(chan responseAndError) pc.reqch <- requestAndChan{ req: req.Request, ch: resc, addedGzip: requestedGzip, continueCh: continueCh, callerGone: gone, }
在该函数中,将 request 和接收请求的 ch 传入到 reqch,把 writeRequest 写入到 writech 。
- writeloop 从 writech 中收到了写请求,会把内容写入到 conn 上,这个请求也就发给 server 端了
- readloop 收到 requestAndChan 结果,上面 writeloop 相当于已经把请求数据发送到 server 端,readloop 这时候可以从 conn 上读出 server 发回的 response 数据,所以 readloop 主要做的就是 ReadResponse,然后把 response 的内容写入到 requestAndChan.ch 中 。
- 主协程只要监听 requestAndChan.ch 来接收相应的 response 即可(用 select 同时监听 err、连接关闭等 chan) 。
for { testHookWaitResLoop() select { case err := <-writeErrCh: // 往 server 端写数据异常 if debugRoundTrip { req.logf("writeErrCh resv: %T/%#v", err, err) } if err != nil { pc.close(fmt.Errorf("write error: %v", err)) return nil, pc.mapRoundTripError(req, startBytesWritten, err) } if d := pc.t.ResponseHeaderTimeout; d > 0 { if debugRoundTrip { req.logf("starting timer for %v", d) } timer := time.NewTimer(d) defer timer.Stop() // prevent leaks respHeaderTimer = timer.C } case <-pc.closech: // 连接关闭异常 if debugRoundTrip { req.logf("closech recv: %T %#v", pc.closed, pc.closed) } return nil, pc.mapRoundTripError(req, startBytesWritten, pc.closed) case <-respHeaderTimer: // 读请求头超时 if debugRoundTrip { req.logf("timeout waiting for response headers.") } pc.close(errTimeout) return nil, errTimeout case re := <-resc: // 正常地从 response 的 channel 里读到了响应数据 if (re.res == nil) == (re.err == nil) { panic(fmt.Sprintf("internal error: exactly one of res or err should be set; nil=%v", re.res == nil)) } if debugRoundTrip { req.logf("resc recv: %p, %T/%#v", re.res, re.err, re.err) } if re.err != nil { return nil, pc.mapRoundTripError(req, startBytesWritten, re.err) } return re.res, nil case <-cancelChan: // 用户侧通过 context 取消了流程 pc.t.CancelRequest(req.Request) cancelChan = nil case <-ctxDoneChan: // 这个应该意思差不多 pc.t.cancelRequest(req.Request, req.Context().Err()) cancelChan = nil ctxDoneChan = nil } }
http2https://tools.ietf.org/html/rfc7540 https://github.com/bagder/http2-explained文章插图
4
http2 协议通过 frame 中的 stream id 对请求和响应进行关联 。
http2 可以不等待上一个请求响应后再发下一个请求,因此同一个连接上可以实现 multiplexing 。标准库中对于 http2 连接的处理复用了 http1 的连接池逻辑,只不过从连接池中取连接时,并没有真的从连接池里把这个连接拿走 。获取到的连接依然保留在 connpool 中 。
推荐阅读
- 快充|等不及发布会!realme Q5i抢先开售:天玑810+5000mAh电池
- C语言的main函数的三个要点
- C语言的编译机制:分制原则与三种文件
- 微软承认Windows 10新BUG:错误显示没有网络连接
- MySQL 5.6--------SSL连接最佳实战
- GO 切片实力踩坑
- 无线投屏器怎么连接电视和手机?用手机怎么无线投屏到投影仪上?
- 我用 Go语言 生成的随机数为什么不随机?随机数是怎样产生的
- 什么是RPC?RPC有什么用?与语言有关吗?
- 华为b2手环与华为手机的连接方法