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

本文涉及的产品
性能测试 PTS,5000VUM额度
简介: 为什么使用 golang http包 会把 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

2、修改 limits.conf 文件

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

vim /etc/security/limits.conf

3、修改 login 文件

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

session   required   pam_limits.so

例如上面这样添加

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

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

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

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

找到问题解决问题

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

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 包对于连接的管理 我们还没有去熟悉他,对于他关于连接的具体实现和细节代码,日后有机会再分享

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

欢迎点赞,关注,收藏

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

好了,本次就到这里

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

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

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
10天前
|
人工智能
AI绘画,Stable Diffusion如何使用中文简体包,黑色页面切换参数http://127.0.0.1:7860/?__theme=dark 两个__,中文包下载和安装
AI绘画,Stable Diffusion如何使用中文简体包,黑色页面切换参数http://127.0.0.1:7860/?__theme=dark 两个__,中文包下载和安装
|
17天前
|
SQL NoSQL Go
技术经验分享:Golang标准库:errors包应用
技术经验分享:Golang标准库:errors包应用
12 0
|
24天前
|
缓存 网络协议 应用服务中间件
深入理解 web 协议(一)- http 包体传输
深入理解 web 协议(一)- http 包体传输
|
25天前
|
监控 小程序 前端开发
基础入门-抓包技术&HTTPS协议&WEB&封包监听&网卡模式&APP&小程序
基础入门-抓包技术&HTTPS协议&WEB&封包监听&网卡模式&APP&小程序
|
27天前
|
移动开发 Go
golang bufio包怎么用?
`bufio` 是 Go 语言中用于提高 I/O 性能的包,它通过使用缓冲区减少对低效磁盘 I/O 操作的调用。简而言之,`bufio` 提供带缓冲的读写功能,减少读取或写入文件时的系统调用次数,从而提升程序性能。
|
2月前
|
Android开发 数据安全/隐私保护 iOS开发
ios和安卓测试包发布网站http://fir.im的注册与常用功能
ios和安卓测试包发布网站http://fir.im的注册与常用功能
37 0
ios和安卓测试包发布网站http://fir.im的注册与常用功能
|
2月前
|
存储 网络协议 Go
7天玩转 Golang 标准库之 http/net
7天玩转 Golang 标准库之 http/net
34 2
|
2月前
|
缓存 安全 Linux
【探索Linux】P.33(HTTP协议)
【探索Linux】P.33(HTTP协议)
49 7
|
2月前
|
Go
深度探讨 Golang 中并发发送 HTTP 请求的最佳技术
深度探讨 Golang 中并发发送 HTTP 请求的最佳技术
|
2月前
|
监控 Go 开发者
Golang深入浅出之-Goroutine泄漏检测与避免:pprof与debug包
【5月更文挑战第2天】本文介绍了Go语言并发编程中可能遇到的Goroutine泄漏问题,以及如何使用`pprof`和`debug`包来检测和防止这种泄漏。常见的问题包括忘记关闭channel和无限制创建goroutine。检测方法包括启动pprof服务器以监控Goroutine数量,使用`debug.Stack()`检查堆栈,以及确保每个Goroutine有明确的结束条件。通过这些手段,开发者可以有效管理Goroutine,维持程序性能。
106 7