Golang HttpClient调用过程浅析 作者: nbboy 时间: 2021-09-26 分类: 默认分类,软件架构,软件工程,设计模式,Golang Golang Client的实现高低版本的实现方式上存在一些有意思的差异,本文主要分析1.0版本和1.2版本,因为1.2版本之后的版本和1.2版本模型上基本是一致的。 先看下1.0版本的调用流程图:  get请求最后会调用核心的RoundTrip,其完成实际的httpclient链接池请求。 ```go func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) ``` 在继续分析剩下的流程之前,先看下主要的Transport结构: ```go type Transport struct { lk sync.Mutex idleConn map[string][]*persistConn ... } // persistConn wraps a connection, usually a persistent one // (but may be used for non-keep-alive requests as well) type persistConn struct { t *Transport cacheKey string // its connectMethod.String() conn net.Conn br *bufio.Reader // from conn bw *bufio.Writer // to conn reqch chan requestAndChan // written by roundTrip(); read by readLoop() ... broken bool // an error has happened on this connection; marked broken so it's not reused. } ``` 链接池通过map结构维护,而key的计算方式是: ```go func (ck *connectMethod) String() string { proxyStr := "" if ck.proxyURL != nil { proxyStr = ck.proxyURL.String() } return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|") } ``` 也就是同样的url是一组,共用一组链接池。 再把视线切回到导图里来,在这个版本中,会用getConn获取到当前链接的一个persistConn(如果是有空闲的persistConn就直接获取,否则就新创建一个persistConn),然后会调用persistConn的roundTrip方法,而其实际是往channel中写入请求,请求服务是在persistConn.readLoop中去做的。这时候的模型其实是这样的:  **一个persistConn有一个goroutine去等待着获取请求,然后向服务发起这个请求,并且把响应通过chan回送到调用方**。可以说**这个过程是异步的**,充分利用了golang的goroutine特性。 到了1.2版本,调用流程变得复杂一些了,还是先看导图:  前面调用过程是一样的,主要区别还是从原来的一个persistConn一个worker变成了**一个persistConn两个worker,这两个worker一个用来写request,一个用来读response**。这样效率肯定会高一点,它的工作模型变成如下图所示:  从1.2之后,模型大体上没有变化,只是对细节上做了一些更进一步的优化和修复问题。 #### 总结 第一个点,从一个client库中可以看出golang基础库的高效,也充分利用了golang的特性-goroutine。第二点,从后面版本开始,读写其实是分离的,这个点我们可以借鉴到我们的设计中去,特别是网络程序。 #### 附件 文章写的很匆忙,细节我总结在导图中了,有兴趣的可以下载观看。 [httpclient导图](http://chenxf.org/files/http_client.emmx "httpclient导图") 标签: client, Golang, http
评论已关闭