为什么使用 golang http包 会把 linux 句柄打满?

简介: 最近工作的时候一个接入服务需要测性能测试,万万没想到测出了一个把 linux 句柄打满的问题

最近工作的时候一个接入服务需要测性能测试,万万没想到测出了一个把 linux 句柄打满的问题

具体是什么问题呢,我们一起来看看

正常操作

项目中,有一些 http 请求是这样写的:

  • 请求 https 的地址,为了绕过 tls ,加上了  TLSClientConfig: &tls.Config{InsecureSkipVerify: true} 配置
  • 正常访问我们需要的请求的地址
  • 正常获取我们的期望的数据,正常解析
func main() {
  client := http.Client{
    Transport: &http.Transport{
      TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    },
  }
  resp, err := client.Get("https://www.xxxxxx.com")
  if err != nil {
    fmt.Println("Get err : ", err)
    return
  }
  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)
  fmt.Println(string(body))
}

例如如下是访问百度的结果,没有什么毛病

t# go run main.go
<html>
<head>
        <script>
                location.replace(location.href.replace("https://","http://"));
        </script>
</head>
<body>
        <noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>

发现问题

可是例如这样的请求代码拿去做性能测试的话,我们实际遇到的问题是,linux 句柄数被打满了

句柄数被打满了,简单的思考有如下 2 个初步的可能:

  • linux 句柄数设置过小
  • http 代码没有释放连接

我知道的有如下 3 种方式,可以修改 linux 的句柄数:

1、修改 /etc/profile

直接修改 /etc/profile , 在 该文件最后加上如下语句

ulimit -n 65535

这里举个例子,设置 65535 个句柄数

修改后 执行

 source /etc/profile

查看效果


ulimit -a

image.png

2、修改 limits.conf 文件

直接修改 limits.conf,让其生效

vim /etc/security/limits.conf

image.png

3、修改 login 文件

我们可以在 /etc/pam.d/login 文件中 添加最下面一行

 session   required   pam_limits.so

image.png

例如上面这样添加

上述 第2 和 第3 种方式,需要重新 ssh 进入到服务器,或者重启服务器才可生效

虽然我增大了 linux 句柄数,发现在性能测试中,只是测得可以稍微久一点了,可是最终还是连接数被打满,这是为什么呢?

仔细查看了代码,代码中也有关闭 http 的连接

image.png

那么问题会是处在哪里呢?

找到问题解决问题

仔细查看了代码,只有一个怀疑点了,那就是下面这句话

client := http.Client{
    Transport: &http.Transport{
      TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    },
  }

最初开始使用这句话的时候,目的也只是为了绕过 tls ,并没有考虑太多,现在仔细看看 http.Transport 结构体里面的功能

type Transport struct {
  idleMu       sync.Mutex
  closeIdle    bool                                // user has requested to close all idle conns
  idleConn     map[connectMethodKey][]*persistConn // most recently used at end
  idleConnWait map[connectMethodKey]wantConnQueue  // waiting getConns
  idleLRU      connLRU
  reqMu       sync.Mutex
  reqCanceler map[cancelKey]func(error)
  altMu    sync.Mutex   // guards changing altProto only
  altProto atomic.Value // of nil or map[string]RoundTripper, key is URI scheme
  connsPerHostMu   sync.Mutex
  connsPerHost     map[connectMethodKey]int
  connsPerHostWait map[connectMethodKey]wantConnQueue // waiting getConns
    ...........省略多行   ...........
    // TLSClientConfig specifies the TLS configuration to use with
  // tls.Client.
  // If nil, the default configuration is used.
  // If non-nil, HTTP/2 support may not be enabled by default.
  TLSClientConfig *tls.Config
  // TLSHandshakeTimeout specifies the maximum amount of time waiting to
  // wait for a TLS handshake. Zero means no timeout.
  TLSHandshakeTimeout time.Duration
  // DisableKeepAlives, if true, disables HTTP keep-alives and
  // will only use the connection to the server for a single
  // HTTP request.
  //
  // This is unrelated to the similarly named TCP keep-alives.
  DisableKeepAlives bool
    ...........省略多行   ...........
    }

仔细查看了上述结构之后,发现  DisableKeepAlives 可以禁用长连接,,每个请求都会创建一个连接,切请求完就会马上关闭连接

正确设置 Transport 后问题得以解决

client := http.Client{
    Transport: &http.Transport{
      TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
      DisableKeepAlives: true,
    },
  }

该问题表象看上去是没有设置好 http.Transport

实际上是 go http 包对于连接的管理 我们还没有去熟悉他,对于他关于连接的具体实现和细节代码,日后有机会再分享

代码修改完毕,性能测试果然正常通过,对技术对代码一定要有敬畏之心


欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

image.png

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~

相关文章
|
3月前
|
设计模式 Kubernetes Go
​​什么是Golang项目的“主包精简,逻辑外置”?​
“主包精简,逻辑外置”是Go语言项目的一种设计原则,强调将程序入口保持简单,核心逻辑拆分至其他包,以提升代码可维护性、可测试性及扩展性,适用于CLI工具、Web服务等场景。
90 7
|
5月前
|
安全 网络协议 Linux
Linux网络应用层协议展示:HTTP与HTTPS
此外,必须注意,从HTTP迁移到HTTPS是一项重要且必要的任务,因为这不仅关乎用户信息的安全,也有利于你的网站评级和粉丝的信心。在网络世界中,信息的安全就是一切,选择HTTPS,让您的网站更加安全,使您的用户满意,也使您感到满意。
150 18
|
5月前
|
Go
在golang中发起http请求以获取访问域名的ip地址实例(使用net, httptrace库)
这只是追踪我们的行程的简单方法,不过希望你跟着探险家的脚步,即使是在互联网的隧道中,也可以找到你想去的地方。接下来就是你的探险之旅了,祝你好运!
207 26
|
6月前
|
中间件 Go
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
234 35
|
6月前
|
JSON API Go
Golang工程组件:自定义HTTP规则的grpc-gateway选项
总的来说,grpc-gateway提供了一种简单有效的方式来为你的gRPC服务提供RESTful风格的API。通过自定义HTTP规则,你可以灵活地定义你的API的行为,以满足你的应用的需求。
141 27
|
Go
Golang的math包常用方法
这篇文章介绍了Golang的math包中的常量和常用方法,并通过示例代码展示了如何使用这些常量和方法。
304 87
Golang的math包常用方法
|
12月前
|
安全 关系型数据库 MySQL
Linux下安装mysql8.0(以tar.xz包安装--编译安装)
通过上述步骤,您完成了从下载、编译、安装到配置MySQL 8.0的全过程。此过程虽然较为复杂,但提供了对MySQL安装环境的完全控制,有助于满足特定的部署需求。在实际操作中,根据具体的系统环境,可能还需调整部分步骤或解决未预见的依赖问题。始终参考官方文档和社区资源,保持安装过程与最新版本的兼容性。
4232 68
|
10月前
|
Ubuntu Linux Go
golang编译成Linux可运行文件
本文介绍了如何在 Linux 上编译和运行 Golang 程序,涵盖了本地编译和交叉编译的步骤。通过这些步骤,您可以轻松地将 Golang 程序编译成适合 Linux 平台的可执行文件,并在目标服务器上运行。掌握这些技巧,可以提高开发和部署 Golang 应用的效率。
1407 14
|
11月前
|
Web App开发 Linux 应用服务中间件
【DrissionPage】Linux上如何将https改为http
通过上述步骤,可以在Linux上将DrissionPage从HTTPS改为HTTP。关键在于修改DrissionPage配置、代码中的HTTPS设置、URL以及Web服务器配置,确保所有部分都正确使用HTTP协议。通过合理配置和测试,能够确保系统在HTTP环境下稳定运行。
370 1