【问题标题】:Go client program generates a lot a sockets in TIME_WAIT stateGo 客户端程序生成大量处于 TIME_WAIT 状态的套接字
【发布时间】:2019-04-02 05:58:11
【问题描述】:

我有一个 Go 程序,它从多个 goroutine 生成大量 HTTP 请求。运行一段时间后,程序吐出错误:connect: cannot assign requested address。

当检查netstat 时,我在TIME_WAIT 中获得大量(28229)个连接。

TIME_WAIT 的大量套接字发生在我的 goroutine 数量为 3 并且严重到足以导致崩溃为 5 时。

我在 docker 下运行 Ubuntu 14.4 并运行 1.7 版

这是 Go 程序。

package main

import (
        "io/ioutil"
        "log"
        "net/http"
        "sync"
)
var wg sync.WaitGroup
var url="http://172.17.0.9:3000/";
const num_coroutines=5;
const num_request_per_coroutine=100000
func get_page(){
        response, err := http.Get(url)
        if err != nil {
                log.Fatal(err)
        } else {
                defer response.Body.Close()
                _, err =ioutil.ReadAll(response.Body)
                if err != nil {
                        log.Fatal(err)
                }
        }

}
func get_pages(){
        defer wg.Done()
        for i := 0; i < num_request_per_coroutine; i++{
                get_page();
        }
}

func main() {
        for i:=0;i<num_coroutines;i++{
                wg.Add(1)
                go get_pages()
        }
        wg.Wait()
}

这是服务器程序:

package main

import (
    "fmt"
    "net/http"
    "log"
)
var count int;
func sayhelloName(w http.ResponseWriter, r *http.Request) {
    count++;
    fmt.Fprintf(w,"Hello World, count is %d",count) // send data to client side
}

func main() {
    http.HandleFunc("/", sayhelloName) // set router
    err := http.ListenAndServe(":3000", nil) // set listen port
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

【问题讨论】:

  • TIME_WAIT 是关闭关闭连接后的正常 TCP 状态。你到底想在这里测试什么?
  • JimB,我正在尝试对 Web 服务器 172.17.0.9:3000 进行压力测试,我想只使用一台客户端计算机来进行。我知道这是可能的,因为如果我将 num_coroutines 设置为 2 就没有问题。但我想使用很多协程
  • 您打开和关闭连接的速度对您的服务器来说太快了。您正在测试的服务器是否预计会重用 http/1.1 连接,还是会在每次请求时关闭连接?
  • JimB,服务器程序非常简单 - 我添加到问题中。我认为它没有使用保持活动连接。
  • 不,服务器默认使用http/1.1。问题的部分原因是服务器太简单并且没有真正做任何工作,并且对“hello world”进行基准测试并不能证明任何事情,因为客户端与服务器一样被测试,并且将来自操作系统和网络的问题混为一谈堆。 (另见stackoverflow.com/questions/30352725)。

标签: http go client time-wait


【解决方案1】:

默认的 http.Transport 太快地打开和关闭连接。由于所有连接都指向相同的主机:端口组合,因此您需要增加 MaxIdleConnsPerHost 以匹配 num_coroutines 的值。否则,传输将频繁关闭额外的连接,然后立即重新打开它们。

您可以在默认传输上全局设置:

http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = numCoroutines

或者在创建自己的交通工具时

t := &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    DialContext: (&net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
    }).DialContext,
    MaxIdleConnsPerHost:   numCoroutines,
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   10 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
}

类似问题:Go http.Get, concurrency, and "Connection reset by peer"

【讨论】:

  • JimB,我使用了上面的第一个选项,它极大地改善了程序的行为。现在它不会因 num_conections 的数量少而崩溃,但我确实会因数量多(例如 10000)而中断。我会尝试更详细的选项,看看它有更多帮助。
  • @yigal:当然,如果您将并发性提高到足够高,它会中断,通过环回测试单个 http 客户端和服务器的 10000 个并发连接有什么意义?你只有这么多的文件描述符和临时端口,你可以在没有一些系统调整和更好的配置的情况下使用。
  • 我们的想法是仅使用一台客户端机器对我们的系统进行压力测试。单个客户端机器优于多个客户端机器的优点是应该更简单地开发和测试压力测试代码。为此,我正在尝试使用 golang,因为它是一种快速语言,用于生成线程/协程的开销较低。我并不完全精通 linux 优化,但我的逻辑表明使用普通 Linux 应该可以实现 10000 个并发连接。我只需要更有效地处理 TIME_WAIT 问题。
  • 我们做 POST 请求有什么不同吗?
  • 如何以这种方式为每个请求设置不同的代理?有可能吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-19
  • 1970-01-01
相关资源
最近更新 更多