1. Body io.ReadCloser
  2.  
     
  3.  
    The http Client and Transport guarantee that Body is always non-nil, even on
  4.  
    responses without a body or responses with a zero-length body. It is the caller's
  5.  
    responsibility to close Body. The default HTTP client's Transport does not attempt to
  6.  
    reuse HTTP/1.0 or HTTP/1.1 TCP connections ("keep-alive") unless the Body is read to
  7.  
    completion and is closed.
  8.  
     
  9.  
    http客户端(Client)和传输(Transport)保证响应体总是非空的,即使响应没有响应体或0长响应
  10.  
    体。关闭响应体是调用者的责任。默认http客户端传输(Transport)不会尝试复用keep-alive的
  11.  
    http/1.0、http/1.1连接,除非请求体已被完全读出而且被关闭了。

以上是http包文档说明。但是为什么body需要被关闭呢,不关闭会如何?那就读源码呗。

要了解body,首先要了解http事务是如何处理的。http事务是交由底层的Transport处理的。

第一步是从连接池获取一个连接,这个连接的功能由3个goroutine协同实现,一个主goroutine,一个readLoop,一个writeLoop,后两个goroutine生命周期和连接一致。虽说readLoop和writeLoop名字叫循环(也确实是for循环),但实际上一次循环就完整处理一个http事务,循环本身仅仅是为了连接复用,所以为了便于理解其逻辑可以忽略它的循环结构。

接下来三个goroutine协同完成http事务:

  1. 主goroutine将request同时发给readLoop和writeLoop。
  2. writeLoop发送request,然后将状态(error)发送给主goroutine和readLoop。
  3. readLoop解析头部response,然后将状态(error)和response发送给主goroutine。
  4. 主goroutine返回用户代码,readLoop等待body读取完成。
  5. readLoop回收连接。

了解http事务的处理流程,然后我们回过头来看看神秘的body到底是什么

  1.  
    //源码版本1.8.3
  2.  
    // src/net/http/transfer.go:405 body解析方法
  3.  
    func readTransfer(msg interface{}, r *bufio.Reader) (err error)
  4.  
     
  5.  
    // src/net/http/transfer.go:485 解析chunked
  6.  
    t.Body = &body{src: internal.NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
  7.  
     
  8.  
    // src/net/http/transfer.go:490 产生eof
  9.  
    t.Body = &body{src: io.LimitReader(r, realLength), closing: t.Close}
  10.  
     
  11.  
    // src/net/http/transport.go:1560 发送eof信号
  12.  
    body := &bodyEOFSignal{
  13.  
     
  14.  
    // src/net/http/transport.go:1583 gzip解码
  15.  
    resp.Body = &gzipReader{body: body}

body实际上是一个嵌套了多层的net.TCPConn:

  1. bufio.Reader,这层尝试将多次小的读操作替换为一次大的读操作,减少系统调用的次数,提高性能;
  2. io.LimitedReader,tcp连接在读取完body后不会关闭,继续读会导致阻塞,所以需要LimitedReader在body读完后发出eof终止读取;
  3. chunkedReader,解析chunked格式编码(如果不是chunked略过);
  4. bodyEOFSignal,在读到eof,或者是提前关闭body时会对readLoop发出回收连接的通知;
  5. gzipReader,解析gzip压缩(如果不是gizp压缩略过);

从上面可以看出如果body既没有被完全读取,也没有被关闭,那么这次http事务就没有完成,除非连接因超时终止了,否则相关资源无法被回收。

如果请求头或响应头指明Connection: close呢?还是无法回收,因为close表示在http事务完成后断开连接,而事务尚未完成自然不会断开,更不会回收。

从实现上看只要body被读完,连接就能被回收,只有需要抛弃body时才需要close,似乎不关闭也可以。但那些正常情况能读完的body,即第一种情况,在出现错误时就不会被读完,即转为第二种情况。而分情况处理则增加了维护者的心智负担,所以始终close body是最佳选择。

作者:一桶冷水
链接:https://www.jianshu.com/p/407fada3cc9d
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转 [golang]为什么Response.Body需要被关闭的更多相关文章

  1. golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期

    欢迎访问我的个人网站获取更佳阅读排版 golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期 | yoko blog (https://pengrl.com/p/47401/) 本篇文章部 ...

  2. Golang控制goroutine的启动与关闭

    最近在用golang做项目的时候,使用到了goroutine.在golang中启动协程非常方便,只需要加一个go关键字: go myfunc(){ //do something }() 但是对于一些长 ...

  3. 【GoLang】golang context channel 详解

    代码示例: package main import ( "fmt" "time" "golang.org/x/net/context" ) ...

  4. java.lang.IllegalStateException: getWriter() has already been called for this response问题解决

    java.lang.IllegalStateException: getWriter() has already been called for this response问题解决 java.lang ...

  5. JSP最常用的五种内置对象(out,request,response,session,application)

    为了简化开发过程,JSP提供了一些内置对象,它们由容器实现和管理.开发者在JSP页面中无需声明,无需实例化就可使用.主要有out,request,response,session,applicatio ...

  6. OkHttp踩坑记:为何 response.body().string() 只能调用一次?

    想必大家都用过或接触过 OkHttp,我最近在使用 Okhttp 时,就踩到一个坑,在这儿分享出来,以后大家遇到类似问题时就可以绕过去. 只是解决问题是不够的,本文将 侧重从源码角度分析下问题的根本, ...

  7. C# Response 下载

    //TransmitFile实现下载 protected void Button1_Click(object sender, EventArgs e) { /* 微软为Response对象提供了一个新 ...

  8. gprc-java与golang分别实现服务端,客户端,跨语言通信(二.golang实现)

    1.编译器protoc, 下载地址:https://github.com/protocolbuffers/protobuf/releases  (下载对应的版本, 解压后放到go的bin中) 2.安装 ...

  9. 如何保障Go语言基础代码质量?

    为什么要谈这个topic? 实践中,质量保障体系的建设,主要针对两个目标: 一是不断提高目标业务测试覆盖率,保障面向客户的产品质量:二就是尽可能的提高人效,增强迭代效率.而构建全链路质量卡点就是整个体 ...

  10. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

随机推荐

  1. 国产开源数据库OpenGauss的安装运行

    步骤一:OpenGauss 的安装 环境 OS:openEuler 20.03 64bit with ARM 架构:arm64 部署:单机 安装过程 1.环境配置 安装依赖包: yum install ...

  2. JS解混淆

    JS解混淆 最近在整理之前和一些同伴的分享资料,发现时间已经过了好久,特此整理一些有价值的分享记录. JS混淆 学习js混淆可以逆向分析混淆和加密过程,实战可用于爬虫和渗透信息获取 本文档用于初步介绍 ...

  3. vue watch的this 到底是什么?

    正文 watch: { value: (newV, oldV) => { this.a = newV; } } 加入该vue对象中,data 有: { data:{ a:5 } } 那么请问,如 ...

  4. c# unsafe

    前言 c# unsafe可以让我们做一些测试的危险操作. 正文 打开unsafe 功能 在 Visual Studio 开发环境中设置此编译器选项 打开项目的"属性"页. 单击&q ...

  5. Django3.0连接数据库注意点

    需先在应用下的__Init__.py文件中配置 import pymysqlpymysql.version_info=(1, 3, 13, 'final', 0) # 3.0时需要pymysql.in ...

  6. Redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗?

    面试官心理分析 这个也是线上非常常见的一个问题,就是多客户端同时并发写一个 key,可能本来应该先到的数据后到了,导致数据版本错了:或者是多客户端同时获取一个 key,修改值之后再写回去,只要顺序错了 ...

  7. Git中 fork, clone,branch

    一.是什么 fork fork,英语翻译过来就是叉子,动词形式则是分叉,如下图,从左到右,一条直线变成多条直线 转到git仓库中,fork则可以代表分叉.克隆 出一个(仓库的)新拷贝 包含了原来的仓库 ...

  8. 使用纯c#在本地部署多模态模型,让本地模型也可以理解图像

    之前曾经分享过纯c#运行开源本地大模型Mixtral-8x7B 当时使用的是llamasharp这个库和Mixtral的模型在本地部署和推理,前段时间我看到llamasharp更新到了0.11.1版本 ...

  9. 力扣603(MySQL)-连续空余座位(简单)

    题目: 几个朋友来到电影院的售票处,准备预约连续空余座位. 你能利用表 cinema ,帮他们写一个查询语句,获取所有空余座位,并将它们按照 seat_id 排序后返回吗? 对于如上样例,你的查询语句 ...

  10. 大数据时代下的App数据隐私安全

    ​简介:随着信息技术快速发展,大数据为我们带来信息共享.便捷生活的同时,还存在着数据安全问题,主流商业模式下APP面临新的挑战.工信部持续开展APP侵权整治活动,进行了了六批次集中抽检,检查了76万款 ...