乐趣区

关于go:为什么使用-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 包对于连贯的治理 咱们还没有去相熟他,对于他对于连贯的具体实现和细节代码,日后有机会再分享

代码批改结束,性能测试果然失常通过,对技术对代码肯定要有敬畏之心

欢送点赞,关注,珍藏

敌人们,你的反对和激励,是我保持分享,提高质量的能源

好了,本次就到这里

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

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

退出移动版