getConn()

想要进行数据传输首先就是要获取连接。

Untitled Diagram.png

每一个 Request 都会提取出 协议 和目的地址形成 connectMethod ,然后生成一个 ConnMethodKey 用来表示与这台主机所有的连接,如果没有连接,那就新建一个连接。
persistConn 相当于一个数据栈,每次取都会拿最后放上去的连接。
idelConnCh 每次有可用的连接都会第一时间拿给将要使用的 Request ,只有当获得连接的整个过程中都没有空闲的连接,才会使用 dialConn() 来新建连接。

Screen Shot 2019-09-20 at 6.33.45 PM.png

如果这里面有连接那就简单多了,直接取用就可以了,特别的事persistConn 相当于一个数据栈,每次取都会拿最后放上去的连接。每次取用之后都会将连接从 map 中删除

  1. if len(pconns) == 1 {
  2. pconn = pconns[0]
  3. delete(t.idleConn, key)
  4. // 只有一个连接,直接删除 map
  5. } else {
  6. pconn = pconns[len(pconns)-1]
  7. t.idleConn[key] = pconns[:len(pconns)-1]
  8. // 拿取最后的 conn
  9. }

DialConn()

在获取连接中,这个函数新建了 persistConn ,默认情况下上 Dial() 都是使用 net 包中普通的 Dial() 进行拨号,当然我们也可以自己给他指定要使用的 Dial()DialTLS()

Untitled Diagram.png

在进行拨号之后,还新建了两个 gorounite 分别作为读取和写入的入口。

persistconn.roundTrip()

获取连接之后,直接调用 persistconn.roundTrip() 获得 Response
http prtsistconn roundtrip.png

readLoop()

transport readloop.png

在创建 persistconn 的时候, readLoop()writeLoop() 就已经开始执行了,它会重复读取 reqch 中的内容,然后通过对应的 requestMethodKey 找到对应的 Response,最后通过 responseAndErr 这个 chanle 返回给主 gorountine。

writeLoop()

transport writeloop.png

writeLoop() 则是通过 writech 获得 request 然后使用 早就创建好的 writer 进行传输,返回的错误会通过 writeErrCh 传回主 gorounite 。

主线程中也会通过 select 监听着 writeErrCh 还有 resc 两个 channel ,如果有 response 返回或者 err 不为空都会直接退出。

  1. select {
  2. case err := <-writeErrCh:
  3. if debugRoundTrip {
  4. req.logf("writeErrCh resv: %T/%#v", err, err)
  5. }
  6. if err != nil {
  7. pc.close(fmt.Errorf("write error: %v", err))
  8. return nil, pc.mapRoundTripError(req, startBytesWritten, err)
  9. }
  10. if d := pc.t.ResponseHeaderTimeout; d > 0 {
  11. if debugRoundTrip {
  12. req.logf("starting timer for %v", d)
  13. }
  14. timer := time.NewTimer(d)
  15. defer timer.Stop() // prevent leaks
  16. respHeaderTimer = timer.C
  17. }
  18. case re := <-resc:
  19. if (re.res == nil) == (re.err == nil) {
  20. panic(fmt.Sprintf("internal error: exactly one of res or err should be set; nil=%v", re.res == nil))
  21. }
  22. if debugRoundTrip {
  23. req.logf("resc recv: %p, %T/%#v", re.res, re.err, re.err)
  24. }
  25. if re.err != nil {
  26. return nil, pc.mapRoundTripError(req, startBytesWritten, re.err)
  27. }
  28. return re.res, nil
  29. }