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