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. Python 爬虫初探

    准备部分 0x01 爬虫的简介和价值 a. 简介 自动抓取互联网数据的程序,是基础技术之一 b. 价值 快速提取网络中有价值的信息 0x02 爬虫的开发环境 a. 环境清单 Python3.7 开发环 ...

  2. 重新整理 .net core 实践篇—————应用层[三十]

    前言 简单介绍一下应用层. 正文 应用层用来做什么的呢? 应用层用来做处理api请求的. [HttpPost] public Task<long> CreateOrder([FromBod ...

  3. react native 如何用vs code 进行调试

    前言 以前做react-native 写的文章,在此分享一下. 在react-native 中有两种方式调试,一种是crome 调试,一种是本地调试,接下来介绍的是本地调试. 解决方案 在vs cod ...

  4. 基于locust全链路压测系统

    2021年中旬就计划着搭建一套压测系统,大约9月份已经搭建完成,使用至今还是比较稳定了,分享一下搭建思路及过程: 为什么选择Locust呢,因为Locust可以仅需要执行命令就可以完成压测任务,并且集 ...

  5. mockjs 模拟实现增删改查

    /*mUtils.js用于解析get请求的参数*/ export const param2Obj = url => { const search = url.split('?')[1] if ( ...

  6. 技术解读:Dragonfly 基于 P2P 的智能镜像加速系统 | 龙蜥技术

    简介: 结合 Dragonfly 子项目 Nydus 进行按需加载可以最大限度提升镜像下载速度. 编者按:上世纪末期,基于 C/S 模式的思想,人们发展了 HTTP . FTP 等应用层协议.然而 C ...

  7. 最佳实践丨构建云上私有池(虚拟IDC)的5种方案详解

    ​简介:云上私有池系列终篇终于来了,本文将重点介绍构建云上的私有池(虚拟IDC)的多种方案和各自的优缺点,并给出相关的性价比优化建议. 本文作者:阿里云技术专家李雨前 摘要 围绕私有池(虚拟IDC)的 ...

  8. [FAQ] mogodb Robo3T 客户端全屏后 怎么退出全屏

    mongodb 的 Robo3T 客户端: 如果是不小心全屏了,不用担心,按 F11 恢复. 如果想要全屏,也是按 F11. Link:https://www.cnblogs.com/farwish/ ...

  9. dotnet C# 推荐一个适合新手入门阅读学习的控制台游戏项目

    对于 C# 编程新手,学习语法和框架是必要的,但是如何将它们灵活地运用到实际项目中,是一个更高层次的挑战.如果只是死记硬背语法规则和框架用法,而没有足够的编程实践,很难提高编程水平和逻辑思维.这个时候 ...

  10. 使用 Uno Islands 在现有 WPF 里面嵌入 Uno 框架

    随着 2022 9 月份 Uno 发布了 4.5 版本,现有的 WPF 应用多了一个新的开发模式,那就是通过 Uno Islands 技术,在现有的 WPF 应用里面嵌入 Uno 应用.通过此方式可以 ...