前言

上一篇文章我们讲了 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连接的建立:

启动一个goroutinetcp的建连,最终调用dialConn方法,在这个方法内做持久化连接,调用net库的dial方法进行TCP连接:

在连接建立后,代码中我们我们还看到分别启动了两个goroutine

  • readLoop用于从tcp连接中读取数据,
  • writeLoop用于从tcp连接中写入数据;

writeLoop方法监听writech通道,在循环中写入发送的数据。

net/http.Transport{}中提供了连接池配置参数,可以自定义

处理 HTTP 请求

net/http.persistConn.roundTrip() 处理HTTP请求


有两个通道:

  • pc.writech :其类型是chan writeRequestwriteLoop协程会循环写入数据,net/http.Request.write会根据net/http.Request结构中的字段按照HTTP协议组成TCP数据段,TCP协议栈会负责将HTTP请求中的内容发送到目标服务器上;
  • pc.reqch:其类型是chan requestAndChanreadLoop协程会循环读取响应数据并且调用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是一个接口类型,具体的是实现是Transporthttp2TransportfileTransport,这样实现扩展性变得很高。

HTTP在建立连接时会耗费大量的资源,需要开辟一个goroutine去创建TCP连接,连接建立后会在创建两个goroutine用于HTTP请求的写入和响应的解析,然后使用channel进行通信,所以要合理利用连接池,避免大量的TCP连接的建立可以优化性能;

net/http 库的客户端实现(下)相关推荐

  1. EF跨库查询,DataBaseFirst下的解决方案

    EF跨库查询,DataBaseFirst下的解决方案 参考文章: (1)EF跨库查询,DataBaseFirst下的解决方案 (2)https://www.cnblogs.com/zyug/p/586 ...

  2. crypto安装_CryptoPP库在Linux系统下的安装与测试

    CryptoPP库在Linux系统下的安装方法比较简单,具体如下: (1)解压源代码压缩包 unzip –a cryptopp700.zip 此处安装的CryptoPP库版本为7.0.0. (2)执行 ...

  3. linux共享库的运行方式,Linux下动态共享库加载及使用详解

    对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似"error while loading shared libraries"这样的错误,这是典型的因为需要的动态库不在动态链接器 ...

  4. 猿题库 iOS 客户端架构设计-唐巧

    序 猿题库是一个拥有数千万用户的创业公司,从20013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个大版本和数十个小版本的高速迭代. 在如此快速的开发过程中,如何保证代码 ...

  5. 使用WinIO库实现保护模式下的IO和内存读写

    问题已解决: 原因是函数的调用方式与WinIO中不一致,使用的时候漏掉了__stdcall. 函数原定义为: 在实际的GPIO读写中遇到以下问题: SetPortVal可正常写入,但是GetPortV ...

  6. ffmpeg-0.6.3开源编码解码库,从linux下移植到windows vs2005,全部开源。

    ffmpeg-0.6.3开源编码解码库,从linux下移植到windows vs2005,全部开源. 需要 Intel C++ Compile 和 开源的SDL库支持,由于 Intel C++ Com ...

  7. 多库多表场景下使用 Amazon EMR CDC 实时入湖最佳实践

    一.前言CDC(Change Data Capture) 从广义上讲所有能够捕获变更数据的技术都可以称为 CDC,但本篇文章中对 CDC 的定义限定为以非侵入的方式实时捕获数据库的变更数据.例如:通过 ...

  8. 实践干货!猿题库 iOS 客户端架构设计

    序 猿题库是一个拥有数千万用户的创业公司,从2013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个大版本和数十个小版本的高速迭代.在如此快速的开发过程中,如何保证代码的质 ...

  9. 猿题库 iOS 客户端架构设计

    推荐序 我几周前写过一篇文章,叫 <被误解的 MVC 和被神化的 MVVM>,其中的很多思想是和本文的作者 Lancy 交流获得的.当时很多人回复问:能直接上猿题库的代码吗?这次 Lanc ...

最新文章

  1. javascript_治愈JavaScript疲劳的研究计划
  2. 视图属性+对象动画组件ViewPropertyObjectAnimator
  3. SpringMVC自定义视图 Excel视图和PDF视图
  4. adf可以自定义溶剂吗_ADF Faces。 立即的自定义客户端事件
  5. jQery 操作CSS
  6. 现代企业三大目标才是核心
  7. 如何将win10的资源管理器指向“这台电脑”?
  8. linux shell脚本读取配置文件 val=1,shell脚本
  9. C语言圆周率天书简化,c语言天书__圆周率的计算及分析
  10. DDSM数据处理之PngWithOverlay 框出病灶区域
  11. 遗传算法的基本原理和方法(转)
  12. studio 3t MongoDB for MAC 201903+Cracking
  13. SpringBoot集成微信支付V3
  14. mongodb $符号的神奇用法+mongo数据类型
  15. 一个实验了解多层内网渗透
  16. 2021年中国内燃机曲轴行业现状及竞争格局分析,高壁垒塑造曲轴行业高集中度,新能源汽车带动行业发展「图」
  17. 使用eclips创建Maven项目
  18. 【Axure教程】中继器表格寻找和标记数据
  19. Unknown tag (c:forEach) 未知的标签 解决方法
  20. VCIP2020:相同主观质量下基于学习的UGC短视频低码率编码

热门文章

  1. 自己写的微信投票系统的小功能
  2. 不负青春韶华,为不平凡的我们加油
  3. 校企合作计算机专业共建协议书,校企合作共建实训基地协议书
  4. 用JavaScript获取网页中的js、css、Flash等文件
  5. html中加入特殊字体,HTML加载特殊字体
  6. Android应用.三星i9000系列(3).无需刷机轻松获取Root权限
  7. Oracle DG主备切换VIP
  8. 人民币记账中,数字中间的逗号是什么意思?
  9. 可以测试打字的手机软件,手机打字软件哪款好用?4款打字软件推荐
  10. “no CUDA-capable device is detected”的解决方法