当前位置: 首页 > news >正文

法律服务网站建设中小型网站建设流程

法律服务网站建设,中小型网站建设流程,吴桥县网站建设价格,软件开发平台方案参考: 知乎:别因为忘记close你的httpclient,造成goroutine泄漏 CSDN:resp.Body.Close() 引发的内存泄漏goroutine个数 先来看几道题,想一想最终的输出结果是多少呢? package mainimport ("fmt"…

参考:

  • 知乎:别因为忘记close你的httpclient,造成goroutine泄漏
  • CSDN:resp.Body.Close() 引发的内存泄漏goroutine个数

先来看几道题,想一想最终的输出结果是多少呢?

package mainimport ("fmt""io/ioutil""net/http""runtime"
)// 测试1:不执行resp.Body.Close(),也不执行ioutil.ReadAll(resp.Body)
func testFun1() {for i := 0; i < 5; i++ {_, err := http.Get("https://www.baidu.com")if err != nil {fmt.Println("testFun1 http.Get err: ", err)return}}time.Sleep(time.Second * 1)fmt.Println("testFun1() 当前goroutine数量=", runtime.NumGoroutine())
}// 测试2:执行resp.Body.Close(),不执行ioutil.ReadAll(resp.Body)
func testFun2() {for i := 0; i < 5; i++ {resp, err := http.Get("https://www.baidu.com")if err != nil {fmt.Println("testFun2 http.Get err: ", err)return}resp.Body.Close() // 执行resp.Body.Close()}// Close()过程需要一定时间,如果直接输出goroutine数量,可能出现连接还未完全回收的情况,结果有时为1有时为3// 因此,为了结果的准确性,我们这里休眠等待1秒,使得连接完全被回收。time.Sleep(time.Second * 1)fmt.Println("testFun2() 当前goroutine数量=", runtime.NumGoroutine())
}// 测试3:不执行resp.Body.Close(),执行ioutil.ReadAll(resp.Body)
func testFun3() {for i := 0; i < 5; i++ {resp, err := http.Get("https://www.baidu.com")if err != nil {fmt.Println("testFun3 http.Get err: ", err)return}_, _ = ioutil.ReadAll(resp.Body) // 执行ioutil.ReadAll(resp.Body)}time.Sleep(time.Second * 1)fmt.Println("testFun3() 当前goroutine数量=", runtime.NumGoroutine())
}// 测试4:执行resp.Body.Close(),也执行ioutil.ReadAll(resp.Body)
func testFun4() {for i := 0; i < 5; i++ {resp, err := http.Get("https://www.baidu.com")if err != nil {fmt.Println("testFun4 http.Get err: ", err)return}_, _ = ioutil.ReadAll(resp.Body) // 执行ioutil.ReadAll(resp.Body)resp.Body.Close()                // 执行resp.Body.Close()}time.Sleep(time.Second * 1)fmt.Println("testFun4() 当前goroutine数量=", runtime.NumGoroutine())
}func main() {testFun1()testFun2()testFun3()testFun4()
}

答案:

  • testFun1() 当前goroutine数量= 11
  • testFun2() 当前goroutine数量= 1
  • testFun3() 当前goroutine数量= 3
  • testFun4() 当前goroutine数量= 3

注意:

针对以上testFun2(),如果仅仅执行了 resp.Body.Close(),那么为了结果的准确性,我们在打印结果之前通过time.Sleep(time.Second * 1)先休眠等待1秒,使得连接完全被回收后,然后再打印输出结果
因为,Close()过程可能需要一定时间,如果直接输出goroutine数量,那么可能出现连接还未完全回收的情况,导致结果随机,有时为1有时为3 …

而针对以上testFun4(),虽然执行了 resp.Body.Close(),但是同时也执行了 ioutil.ReadAll(resp.Body),这里会优先把当前的连接放入空闲列表中,供下次复用,所以不存在输出结果随机的情况。

解析:

首先要清楚,如果没有调用 resp.Body.Close(),也就是没有 回收/释放 连接,那一定会存在协程的泄漏问题(进而导致内存泄漏)。

另外,稍微了解 go net/http 包的同学,都知道 每次执行http的 Get/Post 请求时,底层都会创建两个协程,分别处理 写请求/读响应 这两个事件。具体底层逻辑后面会提到 …
所以你可以简单的理解为:执行一条http请求时,go内部会创建两个协程。

接下来,针对每一个示例做分析:

  • testFun1() 这里执行了 5 次http请求,且不执行 resp.Body.Close(),也不执行 ioutil.ReadAll(resp.Body)
    因为 每次执行http的 Get/Post 请求时,底层都会创建两个协程,加上主协程本身,所以一共有 5*2 + 1 = 11 个协程。
  • testFun2() 这里执行了 5 次http请求,执行 resp.Body.Close(),不执行 ioutil.ReadAll(resp.Body)
    说明在执行 resp.Body.Close()后,回收了底层都会创建两个协程,只剩下主协程本身,所以一共就 1 个协程。
  • testFun3() 这里执行了 5 次http请求,不执行 resp.Body.Close(),执行 ioutil.ReadAll(resp.Body)
    当执行 ioutil.ReadAll(resp.Body),将 resp.body 读取完之后,会把当前的连接放入空闲列表中,供下次复用
  • testFun4() 这里执行了 5 次http请求,执行 resp.Body.Close(),也执行 ioutil.ReadAll(resp.Body)
    这里的结果和 testFun3() 一致,关键在于 执行了 ioutil.ReadAll(resp.Body),将 resp.body 读取完之后,会优先把当前的连接放入空闲列表中,供下次复用,即使你执行了 resp.Body.Close()

源码解析:

通过跟踪 go net/http 包的源码,得到其调用链路的流程图:

可以发现每次新建立一个http请求,最终底层实际上都会创建两个新的协程(写请求/读响应)

  • go pconn.readLoop():启动一个 读响应 相关的goroutine
  • go pconn.writeLoop():启动一个 写请求 相关的goroutine
    在这里插入图片描述

readLoop()writeLoop()本身都是for循环:

  • 只要【控制是否退出循环的变量 alive】为true,循环就会一直进行下去,且会把当前的连接放入空闲列表中,供下次复用
    • 示例:Fn正常的读body,当body读完之后,会向 waitForBodyRead推入一个true:waitForBodyRead <- isEOF
  • 而只有从 bodyEOF := <-waitForBodyRead中读出的值为false,循环才会退出。
    示例:earlyCloseFn 未读取body就close的会执行此方法,可以发现向 waitForBodyRead推入一个false:waitForBodyRead <- false
    • 若退出循环,最后会执行 readLoop 中的defer()函数。defer函数中的 pc.close(closeErr)不仅会关闭本身的通道closech,也会进而控制 writeLoop的退出。
      在这里插入图片描述

因此,waitForBodyRead这个chan对接下来goroutine的生死起着关键作用。

在这里插入图片描述

总结:

  • 日常开发中,在写代码时基本都不会遗漏 ioutil.ReadAll(resp.Body),但可能会存在忘记写 resp.Body.Close()的情况,这里可能会导致协程泄漏。
    但如果你请求的域名 url 不变的话,那么顶多只会泄漏一个负责 读响应 的goroutine 和一个负责 写请求 的goroutine,不会导致协程数飙升的情况,所以程序运行一般也不会出现什么太明显的问题。
  • 不过还是建议:在执行任何http 请求时,一定要记得加 resp.Body.Close(),避免异常情况下的goroutine泄漏,进而导致内存泄漏(每个goroutine初始时只占几kb,但量多了也扛不住),引起服务异常。
http://www.yayakq.cn/news/286634/

相关文章:

  • 经营范围网站开发运营重庆建设厂
  • 网站建设 每年费用濮阳推广公司
  • 网站建设常见问题及解决办法中国企业500强2022
  • 当今做那些网站能致富网站轮换图片怎么做
  • 网站降权的表现网站建设企业服务器
  • 高端网站建设公司哪家好网站推广的方法是什么
  • 做网站后有人抢注关键词装修网站建设公司
  • 南宁网站建设服务公司公司网站域名备案
  • 服务器上网站建设网站建设后如何修改
  • 网站个人备案需要什么资料数据可视化
  • 投资建设个什么网站好深圳建站公司服务
  • 网站建设 数据库网站建设 东营远见网络公司
  • 网站搜索优化技巧松阳网站建设
  • 印刷东莞网站建设技术支持中国电商网官网
  • gif5网站下载不了做的动图网站布局结构主要分为
  • 遵义网站制作一般需要多少钱wordpress $limit
  • 获取网站缩略图的asp代码wordpress标签加入文章列表
  • 烟台网站制作计划4虎最新域名更新地址
  • 网站很难被百度收录官方微网站吗
  • 中国建设银行开放式网站专业招商代理网址
  • 牛商网做的网站如何学工业设计出来干什么
  • 源码下载网站有哪些郴州宸轩网络科技有限公司
  • 建设厅网站平顶山官方网站下载微信最新版
  • html简单一套网页源代码郑州百度关键词seo
  • 怎么开网站平台挣钱未备案的网站
  • 绵阳住房和城乡建设厅官方网站龙岗注册公司
  • 校级特色专业建设网站广西壮族自治区图书馆
  • 教学网站建设目的湛江市建设局官网站
  • 网站开发的硬件设备网络公司给我做网站我有没有源代码版权吗
  • 我的世界查找建筑网站企业安全文化建设做法