热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Golang应用中频繁出现TIME_WAIT及ESTABLISHED状态的优化与解决策略

在Golang应用中,频繁出现的TIME_WAIT和ESTABLISHED状态可能会导致性能瓶颈。本文探讨了这些状态产生的原因,并提出了优化与解决策略。通过调整内核参数、优化连接管理和使用连接池技术,可以有效减少TIME_WAIT的数量,提高应用的并发处理能力。同时,对于ESTABLISHED状态,可以通过合理的超时设置和错误处理机制,确保连接的高效利用和快速释放。

一、产生大量ESTABLISHED

resp, err := getHttpClientIns().Get(url)
if err != nil {return
}if resp.StatusCode != 200 {return nil, fmt.Errorf(fmt.Sprintf("http code : %d", resp.StatusCode))
}bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {return
}
defer resp.Body.Close()

以上代码看起来貌似没什么问题,但是当resp.StatusCode != 200 的时候,直接返回了,因此 defer resp.Body.Close() 这句代码并没有被调用,也就是产生的tcp连接并没有被关闭。由于代码中会并发的多次请求,如果请求全部都不是200,那么最终都不会Close,便会产生大量的ESTABLISHED状态。

那么我们只需要把defer resp.Body.Close()这一句提前即可,如下:

resp, err := getHttpClientIns().Get(url)
if err != nil {return
}
defer resp.Body.Close()if resp.StatusCode != 200 {return nil, fmt.Errorf(fmt.Sprintf("http code : %d", resp.StatusCode))
}bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {return
}

当然,上面的代码其实依然有问题,后面再说。

二、产生大量的TIME_WAIT

前面说了,当时是并发的去大量的请求,代码类似下面:

for i := 0; i <10; i++{go func(){for i := 0; i <10000000; i++{resp, err := http.Get(url)}}()
}

即开多个线程并发的去请求,然后就发现TIME_WAIT的数量持续上升,很快就导致系统资源耗尽。经查,发现http client的参数MaxConnsPerHost比较小,默认为2,因此调大了连接池的数量。具体请求的代码改成类型下面的样子

timeoutContext, _ := context.WithTimeout(context.Background(), timeOut)req, err := http.NewRequestWithContext(timeoutContext, "GET", url, nil)if err != nil {return nil, err}resp, err := getHttpClientWithMaxPerlHost(maxPerlHostConnect).Do(req)if err != nil {return nil, err}defer resp.Body.Close()if resp.StatusCode != 200 {return nil, fmt.Errorf(fmt.Sprintf("http code : %d", resp.StatusCode))}bytes, err := ioutil.ReadAll(resp.Body)if err != nil {return}

其中getHttpClientWithMaxPerlHost()函数主要是得到一个http clien,我是这样配的:

client = &http.Client{Transport: &http.Transport{Proxy: http.ProxyFromEnvironment,DialContext: (&net.Dialer{Timeout: 5 * time.Second,KeepAlive: 30 * time.Second,}).DialContext,//MaxIdleConns: maxPerlHostConnect*10,IdleConnTimeout: 90 * time.Second,TLSHandshakeTimeout: 10 * time.Second,ExpectContinueTimeout: 1 * time.Second,ResponseHeaderTimeout: 30 * time.Second,MaxConnsPerHost: maxPerlHostConnect,MaxIdleConnsPerHost: maxPerlHostConnect / 2,},

正常情况下,确实可以把TIME_WAIT的数量将下来。但是,当请求全部404之后,我发现仍然会产生大量TIME_WAIT,与正常的有啥不同呢,正常情况下会读取Body里面的内容。

正好网上的一篇文章使用golang的`http.Client`容易出现TIME_WAIT上涨的几种情况和解决方案 - Go语言中文网 - Golang中文社区 (studygolang.com),其中第一点引起了我的注意,我试着当resp.StatusCode != 200的时候,也把Body里面的内容读出来,代码如下:

timeoutContext, _ := context.WithTimeout(context.Background(), timeOut)req, err := http.NewRequestWithContext(timeoutContext, "GET", url, nil)if err != nil {return nil, err}resp, err := getHttpClientWithMaxPerlHost(maxPerHost).Do(req)if err != nil {return nil, err}defer resp.Body.Close()if resp.StatusCode != 200 {io.Copy(ioutil.Discard, resp.Body)return nil, fmt.Errorf(fmt.Sprintf("http code : %d", resp.StatusCode))}bytes, err := ioutil.ReadAll(resp.Body)if err != nil {return}

果然,TIME_WAIT的数量降下来了。

总结:

1、如果单线程请求,默认的http client就够用了,不需要额外的参数配置。

2、如果多线程并发请求,那么需要配置参数,主要是MaxConnsPerHost和MaxIdleConnsPerHost

3、一定要记得调用resp.Body.Close(),关闭打开的Body.

4、一定要把Body里面的内容读出来,就算异常也要读出来丢掉。


推荐阅读
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • 本文探讨了为何相同的HTTP请求在两台不同操作系统(Windows与Ubuntu)的机器上会分别返回200 OK和429 Too Many Requests的状态码。我们将分析代码、环境差异及可能的影响因素。 ... [详细]
  • 本文详细探讨了JavaScript中的作用域链和闭包机制,解释了它们的工作原理及其在实际编程中的应用。通过具体的代码示例,帮助读者更好地理解和掌握这些概念。 ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 主板IO用W83627THG,用VC如何取得CPU温度,系统温度,CPU风扇转速,VBat的电压. ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 本文详细介绍了流编辑器sed中的G、H、g、h命令,探讨了它们的工作原理及应用场景。通过实例解析和图解分析,帮助读者掌握这些高级命令的使用方法。 ... [详细]
  • 简化报表生成:EasyReport工具的全面解析
    本文详细介绍了EasyReport,一个易于使用的开源Web报表工具。该工具支持Hadoop、HBase及多种关系型数据库,能够将SQL查询结果转换为HTML表格,并提供Excel导出、图表显示和表头冻结等功能。 ... [详细]
  • 在 Android 开发中,通过 Intent 启动 Activity 或 Service 时,可以使用 putExtra 方法传递数据。接收方可以通过 getIntent().getExtras() 获取这些数据。本文将介绍如何使用 RoboGuice 框架简化这一过程,特别是 @InjectExtra 注解的使用。 ... [详细]
  • 本文将探讨2015年RCTF竞赛中的一道PWN题目——shaxian,重点分析其利用Fastbin和堆溢出的技巧。通过详细解析代码流程和漏洞利用过程,帮助读者理解此类题目的破解方法。 ... [详细]
  • CSS高级技巧:动态高亮当前页面导航
    本文介绍了如何使用CSS实现网站导航栏中当前页面的高亮显示,提升用户体验。通过为每个页面的body元素添加特定ID,并结合导航项的类名,可以轻松实现这一功能。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • Java多线程实现:从1到100分段求和并汇总结果
    本文介绍如何使用Java编写一个程序,通过10个线程分别计算不同区间的和,并最终汇总所有线程的结果。每个线程负责计算一段连续的整数之和,最后将所有线程的结果相加。 ... [详细]
  • 深入解析Java多线程与并发库的应用:空中网实习生面试题详解
    本文详细探讨了Java多线程与并发库的高级应用,结合空中网在挑选实习生时的面试题目,深入分析了相关技术要点和实现细节。文章通过具体的代码示例展示了如何使用Semaphore和SynchronousQueue来管理线程同步和任务调度。 ... [详细]
author-avatar
LOKYIP2012_862
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有