热门标签 | 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里面的内容读出来,就算异常也要读出来丢掉。


推荐阅读
  • 本文详细介绍了如何正确设置Shadowsocks公共代理,包括调整超时设置、检查系统限制、防止滥用及遵守DMCA法规等关键步骤。 ... [详细]
  • 在尝试通过自定义端口部署Spring Cloud Eureka时遇到了连接失败的问题。本文详细描述了问题的现象,并提供了有效的解决方案,以帮助遇到类似情况的开发者。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • 本文介绍了实时流协议(RTSP)的基本概念、组成部分及其与RTCP的交互过程,详细解析了客户端请求格式、服务器响应格式、常用方法分类及协议流程,并提供了SDP格式的深入解析。 ... [详细]
  • 一、Advice执行顺序二、Advice在同一个Aspect中三、Advice在不同的Aspect中一、Advice执行顺序如果多个Advice和同一个JointPoint连接& ... [详细]
  • linux网络子系统分析(二)—— 协议栈分层框架的建立
    目录一、综述二、INET的初始化2.1INET接口注册2.2抽象实体的建立2.3代码细节分析2.3.1socket参数三、其他协议3.1PF_PACKET3.2P ... [详细]
  • 本文档介绍了如何使用ESP32开发板在STA模式下实现与TCP服务器的通信,包括环境搭建、代码解析及实验步骤。 ... [详细]
  • C# 中创建和执行存储过程的方法
    本文详细介绍了如何使用 C# 创建和调用 SQL Server 存储过程,包括连接数据库、定义命令类型、设置参数等步骤。 ... [详细]
  • 在 Ubuntu 22.04 LTS 上部署 Jira 敏捷项目管理工具
    Jira 敏捷项目管理工具专为软件开发团队设计,旨在以高效、有序的方式管理项目、问题和任务。该工具提供了灵活且可定制的工作流程,能够根据项目需求进行调整。本文将详细介绍如何在 Ubuntu 22.04 LTS 上安装和配置 Jira。 ... [详细]
  • Tomcat SSL 配置指南
    本文详细介绍了如何在 Tomcat 中配置 SSL,以确保 Web 应用的安全性。通过正确的配置,可以启用 HTTPS 协议并保护数据传输的安全。 ... [详细]
  • 本文介绍了蓝牙低功耗(BLE)中的通用属性配置文件(GATT),包括其角色、层次结构、属性、特性和服务等内容。 ... [详细]
  • 电商高并发解决方案详解
    本文以京东为例,详细探讨了电商中常见的高并发解决方案,包括多级缓存和Nginx限流技术,旨在帮助读者更好地理解和应用这些技术。 ... [详细]
  • RTThread线程间通信
    线程中通信在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取& ... [详细]
  • 深入解析Dubbo:使用与源码分析
    本文详细介绍了Dubbo的使用方法和源码分析,涵盖其架构设计、核心特性和调用流程。 ... [详细]
  • 本文介绍了读写锁(RWMutex)的基本概念、实现原理及其在Go语言中的应用。读写锁允许多个读操作并发执行,但在写操作时确保互斥,从而提高并发性能。 ... [详细]
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社区 版权所有