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. 电脑开机时报错No Bootable Device找不到索引的解决方法

      本文介绍笔记本电脑出现No Bootable Device错误提示,且无法开机的多种解决办法. 1 问题产生   最近,笔记本电脑正在正常使用时,突然蓝屏,出现你的设备遇到问题,需要重启.的提示: ...

  2. redis 简单整理——复制的原理[二十三]

    前言 简单介绍一下复制的原理. 正文 在从节点执行slaveof命令后,复制过程便开始运作,下面详细介绍建立 复制的完整流程. 1)保存主节点(master)信息. 执行slaveof后从节点只保存主 ...

  3. 重新整理 mysql 基础篇————— 索引模型[五]

    前言 简单整理一下索引模型. 正文 对我们开发人员来说,索引感觉非常的重要. 因为索引好用,但是不能多建,因为这影响插入,不能少建,因为这影响读取. 有些为了能够多建索引,通过从多个从库中读取数据,再 ...

  4. 力扣597(MySQL)-好友申请Ⅰ:总体通过率(简单)

    题目: 此表没有主键,它可能包含重复项.该表包含发送请求的用户的 ID ,接受请求的用户的 ID 以及请求的日期. 此表没有主键,它可能包含重复项.该表包含发送请求的用户的 ID ,接受请求的用户的 ...

  5. 力扣704(java&python)-二分查找(简单)

    题目: 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. 示例 1: 输入: ...

  6. 力扣566(java)-重塑矩阵(简单)

    题目: 在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据. 给你一个由二维数组 mat 表示的  ...

  7. 力扣121(java&python)-买卖股票的最佳时机(简单)

    题目: 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格. 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票.设计一 ...

  8. 使用EasyCV Mask2Former轻松实现图像分割

    简介: EasyCV可以轻松预测图像的分割谱以及训练定制化的分割模型.本文主要介绍如何使用EasyCV实现实例分割.全景分割和语义分割,及相关算法思想. 作者:贺弘 谦言 临在 导言 图像分割(Ima ...

  9. EMR StarRocks 极速数据湖分析原理解析

    简介:数据湖概念日益火热,本文由阿里云开源大数据 OLAP 团队和 StarRocks 数据湖分析团队共同为大家介绍" StarRocks 极速数据湖分析 "背后的原理. [首月9 ...

  10. Apache Flink在 bilibili 的多元化探索与实践

    简介: bilibili 万亿级传输分发架构的落地,以及 AI 领域如何基于 Flink 打造一套完善的预处理实时 Pipeline. 本文由 bilibili 大数据实时平台负责人郑志升分享,本次分 ...