net/http 库的客户端实现(下)
前言
上一篇文章我们讲了 net/http
库客户端 request 的构建,接下来继续讲构建HTTP
请求之后的处理操作
net/http 库的客户端实现(上)
net/http 库的客户端实现(下)
net/http 库的服务端实现
启动事务
构建 HTTP
请求后,接着需要开启HTTP
事务进行请求并且等待远程响应,以net/http.Client.Do()
方法为例子,理一下它的调用链路:
- net/http.Client.Do()
- net/http.Client.do()
- net/http.Client.send()
- net/http.Send()
- net/http.Transport.RoundTrip()
RoundTrip()
是RoundTripper
类型中的一个的方法,net/http.Transport
是其中的一个实现,可以在net/http/transport.go
文件中找到这个方法,看一下源码。
func (t *Transport) roundTrip(req *Request) (*Response, error) {// 省略for {// 检测 ctx 退出信号select {case <-ctx.Done():req.closeBody()return nil, ctx.Err()default:}// 获取连接,通过使用连接池对资源进行了复用;pconn, err := t.getConn(treq, cm)if err != nil {t.setReqCanceler(cancelKey, nil)req.closeBody()return nil, err}var resp *Responseif pconn.alt != nil {// HTTP/2 path.t.setReqCanceler(cancelKey, nil) // not cancelable with CancelRequestresp, err = pconn.alt.RoundTrip(req)} else {// 处理响应resp, err = pconn.roundTrip(treq)}if err == nil {resp.Request = origReqreturn resp, nil}// Rewind the body if we're able to.req, err = rewindBody(req)if err != nil {return nil, err}}
}
重点看两部分
net/http.Transport.getConn()
获取连接net/http.persistConn.roundTrip()
处理写入HTTP
请求,并在select
中等待响应的返回
获取连接(getConn)
func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persistConn, err error) {req := treq.Requesttrace := treq.tracectx := req.Context()if trace != nil && trace.GetConn != nil {trace.GetConn(cm.addr())}w := &wantConn{cm: cm,key: cm.key(),ctx: ctx,ready: make(chan struct{}, 1),beforeDial: testHookPrePendingDial,afterDial: testHookPostPendingDial,}defer func() {if err != nil {w.cancel(t, err)}}()// 在队列中有闲置连接,直接返回if delivered := t.queueForIdleConn(w); delivered {pc := w.pcreturn pc, nil}cancelc := make(chan error, 1)t.setReqCanceler(treq.cancelKey, func(err error) { cancelc <- err })// 放到队列中等待建立新的连接t.queueForDial(w)// 阻塞等待连接select {case <-w.ready:return w.pc, w.errcase <-req.Cancel:return nil, errRequestCanceledConncase <-req.Context().Done():return nil, req.Context().Err()case err := <-cancelc:if err == errRequestCanceled {err = errRequestCanceledConn}return nil, err}
}
因为连接的建议会消耗比较多的时间,带来较大的开下,所以Go语言使用了连接池对资源进行分配和复用,先调用 net/http.Transport.queueForIdleConn()
获取等待闲置的连接,如果没有获取到在调用net/http.Transport.queueForDial
在队列中等待建立新的连接,通过select
监听连接是否建立完毕,超时未获取到连接会上剖错误,我们继续在queueForDial
追踪TCP
连接的建立:
启动一个goroutine
做tcp
的建连,最终调用dialConn
方法,在这个方法内做持久化连接,调用net
库的dial
方法进行TCP
连接:
在连接建立后,代码中我们我们还看到分别启动了两个goroutine
,
readLoop
用于从tcp
连接中读取数据,writeLoop
用于从tcp
连接中写入数据;
writeLoop
方法监听writech
通道,在循环中写入发送的数据。
net/http.Transport{}
中提供了连接池配置参数,可以自定义
处理 HTTP 请求
net/http.persistConn.roundTrip()
处理HTTP请求
有两个通道:
pc.writech
:其类型是chan writeRequest
,writeLoop
协程会循环写入数据,net/http.Request.write
会根据net/http.Request
结构中的字段按照HTTP
协议组成TCP
数据段,TCP
协议栈会负责将HTTP
请求中的内容发送到目标服务器上;pc.reqch
:其类型是chan requestAndChan
,readLoop
协程会循环读取响应数据并且调用net/http.ReadResponse
进行协议解析,其中包含状态码、协议版本、请求头等内容;
总结
net/http.Client
是级别最高的抽象,其中transport
用于开启HTTP
事务,jar
用于处理cookie
;net/http.Transport
中主要逻辑两部分:- 从连接池中获取持久化连接
- 使用持久化连接处理HTTP请求
net/http
库中默认有一个DefaultClient
可以直接使用,DefaultClient
有对应DefaultTransport
,可以满足大多数场景。
- 如果需要使用自己管理
HTTP
客户端的头域、重定向等策略,可以自定义Client
, - 如果需要管理代理、TLS配置、连接池、压缩等设置,可以自定义
Transport
;
因为HTTP
协议的版本是不断变化的,所以为了可扩展性,transport
是一个接口类型,具体的是实现是Transport
、http2Transport
、fileTransport
,这样实现扩展性变得很高。
HTTP
在建立连接时会耗费大量的资源,需要开辟一个goroutine
去创建TCP
连接,连接建立后会在创建两个goroutine
用于HTTP
请求的写入和响应的解析,然后使用channel
进行通信,所以要合理利用连接池,避免大量的TCP
连接的建立可以优化性能;
net/http 库的客户端实现(下)相关推荐
- EF跨库查询,DataBaseFirst下的解决方案
EF跨库查询,DataBaseFirst下的解决方案 参考文章: (1)EF跨库查询,DataBaseFirst下的解决方案 (2)https://www.cnblogs.com/zyug/p/586 ...
- crypto安装_CryptoPP库在Linux系统下的安装与测试
CryptoPP库在Linux系统下的安装方法比较简单,具体如下: (1)解压源代码压缩包 unzip –a cryptopp700.zip 此处安装的CryptoPP库版本为7.0.0. (2)执行 ...
- linux共享库的运行方式,Linux下动态共享库加载及使用详解
对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似"error while loading shared libraries"这样的错误,这是典型的因为需要的动态库不在动态链接器 ...
- 猿题库 iOS 客户端架构设计-唐巧
序 猿题库是一个拥有数千万用户的创业公司,从20013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个大版本和数十个小版本的高速迭代. 在如此快速的开发过程中,如何保证代码 ...
- 使用WinIO库实现保护模式下的IO和内存读写
问题已解决: 原因是函数的调用方式与WinIO中不一致,使用的时候漏掉了__stdcall. 函数原定义为: 在实际的GPIO读写中遇到以下问题: SetPortVal可正常写入,但是GetPortV ...
- ffmpeg-0.6.3开源编码解码库,从linux下移植到windows vs2005,全部开源。
ffmpeg-0.6.3开源编码解码库,从linux下移植到windows vs2005,全部开源. 需要 Intel C++ Compile 和 开源的SDL库支持,由于 Intel C++ Com ...
- 多库多表场景下使用 Amazon EMR CDC 实时入湖最佳实践
一.前言CDC(Change Data Capture) 从广义上讲所有能够捕获变更数据的技术都可以称为 CDC,但本篇文章中对 CDC 的定义限定为以非侵入的方式实时捕获数据库的变更数据.例如:通过 ...
- 实践干货!猿题库 iOS 客户端架构设计
序 猿题库是一个拥有数千万用户的创业公司,从2013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个大版本和数十个小版本的高速迭代.在如此快速的开发过程中,如何保证代码的质 ...
- 猿题库 iOS 客户端架构设计
推荐序 我几周前写过一篇文章,叫 <被误解的 MVC 和被神化的 MVVM>,其中的很多思想是和本文的作者 Lancy 交流获得的.当时很多人回复问:能直接上猿题库的代码吗?这次 Lanc ...
最新文章
- javascript_治愈JavaScript疲劳的研究计划
- 视图属性+对象动画组件ViewPropertyObjectAnimator
- SpringMVC自定义视图 Excel视图和PDF视图
- adf可以自定义溶剂吗_ADF Faces。 立即的自定义客户端事件
- jQery 操作CSS
- 现代企业三大目标才是核心
- 如何将win10的资源管理器指向“这台电脑”?
- linux shell脚本读取配置文件 val=1,shell脚本
- C语言圆周率天书简化,c语言天书__圆周率的计算及分析
- DDSM数据处理之PngWithOverlay 框出病灶区域
- 遗传算法的基本原理和方法(转)
- studio 3t MongoDB for MAC 201903+Cracking
- SpringBoot集成微信支付V3
- mongodb $符号的神奇用法+mongo数据类型
- 一个实验了解多层内网渗透
- 2021年中国内燃机曲轴行业现状及竞争格局分析,高壁垒塑造曲轴行业高集中度,新能源汽车带动行业发展「图」
- 使用eclips创建Maven项目
- 【Axure教程】中继器表格寻找和标记数据
- Unknown tag (c:forEach) 未知的标签 解决方法
- VCIP2020:相同主观质量下基于学习的UGC短视频低码率编码
热门文章
- 自己写的微信投票系统的小功能
- 不负青春韶华,为不平凡的我们加油
- 校企合作计算机专业共建协议书,校企合作共建实训基地协议书
- 用JavaScript获取网页中的js、css、Flash等文件
- html中加入特殊字体,HTML加载特殊字体
- Android应用.三星i9000系列(3).无需刷机轻松获取Root权限
- Oracle DG主备切换VIP
- 人民币记账中,数字中间的逗号是什么意思?
- 可以测试打字的手机软件,手机打字软件哪款好用?4款打字软件推荐
- “no CUDA-capable device is detected”的解决方法