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

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

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

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                

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

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

  • linux 句柄数设置过小

  • http 代码没有释放连接

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

/etc/profile
ulimit -n 65535

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

修改后 执行

 source /etc/profile

查看效果

ulimit -a

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

vim /etc/security/limits.conf

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

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

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

好了,本次就到这里

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

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