" localhost:没有这样的主持人"在使用ResponseWriter.Write时在Go中连接250个连接之后

时间:2014-10-07 03:33:58

标签: networking go

我有以下http客户端/服务器代码:

服务器

func main() {

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Req: ", r.URL)
        w.Write([]byte("OK")) // <== PROBLEMATIC LINE
        // w.WriteHeader(200) // Works as expected
    })

    log.Fatal(http.ListenAndServe(":5008", nil))
}

客户端

func main() {

    client := &http.Client{}

    for i := 0; i < 500; i++ {
        url := fmt.Sprintf("http://localhost:5008/%02d", i)
        req, _ := http.NewRequest("GET", url, nil)
        _, err := client.Do(req)

        if err != nil {
            fmt.Println("error: ", err)
        } else {
            fmt.Println("success: ", i)
        }

        time.Sleep(10 * time.Millisecond)
    }
}

当我在服务器上面运行客户端时,在250个连接之后,我从client.Do得到以下错误: error: Get http://localhost:5008/250: dial tcp: lookup localhost: no such host并且没有更多的连接会成功。

如果我从w.Write([]byte("OK")) ==&gt;更改服务器中的行w.WriteHeader(200)然而,连接数量没有限制,它可以按预期工作。

我在这里缺少什么?

6 个答案:

答案 0 :(得分:6)

你没有关闭身体。当您从服务器进行任何写入时,连接将保持打开状态,因为尚未读取响应。当你只是WriteHeader时,响应完成,连接可以重用或关闭。

说实话,我不知道为什么离开开放连接会导致域查找失败。基于250非常接近第256轮的事实,我猜想你正在击中的操作系统存在人为限制。也许允许的最大FD是256?看起来很低,但它可以解释这个问题。

func main() {
    client := &http.Client{}

    for i := 0; i < 500; i++ {
        url := fmt.Sprintf("http://localhost:5008/%02d", i)
        req, _ := http.NewRequest("GET", url, nil)
        resp, err := client.Do(req)

        if err != nil {
            fmt.Println("error: ", err)
        } else {
            fmt.Println("success: ", i)
            resp.Body.Close()
        }

        time.Sleep(10 * time.Millisecond)
    }
}

答案 1 :(得分:6)

应用程序必须按照net/http package docmentation开头所述关闭客户端上的响应正文。

func main() {

  client := &http.Client{}

  for i := 0; i < 500; i++ {
    url := fmt.Sprintf("http://localhost:5008/%02d", i)
    req, _ := http.NewRequest("GET", url, nil)
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("error: ", err)
    } else {
        resp.Body.Close() // <---- close is required
        fmt.Println("success: ", i)
    }
    time.Sleep(10 * time.Millisecond)
  }
}

如果应用程序未关闭响应正文,则可能无法关闭底层网络连接或将其返回到客户端的连接池。在这种情况下,每个新请求都会创建一个新的网络连接。该进程最终命中file descriptor limit,任何需要文件描述符的内容都将失败。这包括名称查找和打开新连接。

OS X上打开文件描述符数量的默认限制为256.我希望客户端应用程序在此限制之内失败。

因为服务器的每个连接都使用服务器上的文件描述符,所以服务器也可能已达到其文件描述符限制。

从服务器代码中删除w.Write([]byte("OK"))时,响应正文的长度为零。这将触发客户端中零长度响应主体的优化,其中连接在应用程序关闭响应主体之前关闭或返回到池中。

答案 2 :(得分:1)

同时使用MAC OSX时也会遇到同样的问题:

  

250次请求后会出现该错误;

使用go1.8.3。

我的问题的解决方法是关闭req和res body:

for i := 0; i < 10; i++ {
    res, err := client.Do(req)
    if err == nil {
        globalCounter.Add(1)
        res.Body.Close()
        req.Body.Close()
        break
    } else {
        log.Println("Error:", err, "retrying...", i)
    }
}

答案 3 :(得分:0)

我正在使用go版本go1.9.4 linux / amd64 我尝试了不同的方法来解决此问题。什么都没有帮助 http://craigwickesser.com/2015/01/golang-http-to-many-open-files/

与resp.Body.Close()一起,我不得不添加req.Header.Set(“ Connection”,“ close”)

func PrettyPrint(v interface{}) (err error) {
  b, err := json.MarshalIndent(v, "", "  ")
  if err == nil {
    fmt.Println(string(b))
  }
  return
}

func Json(body []byte, v interface{}) error {
  return json.Unmarshal(body, v)
}

func GetRequests(hostname string, path string) []map[string]interface{} {
transport := &http.Transport{
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    Dial: (&net.Dialer{
        Timeout:   0,
        KeepAlive: 0,
    }).Dial,
    TLSHandshakeTimeout: 10 * time.Second,
}
httpClient := &http.Client{Transport: transport}

req, reqErr := http.NewRequest("GET", "https://"+hostname+path, nil)
req.SetBasicAuth("user", "pwd")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Connection", "close")
if reqErr != nil {
    fmt.Println("error in making request", reqErr)
}

resp, err := httpClient.Do(req)
if err != nil {
    fmt.Println("error in calling request", err)
}
defer resp.Body.Close()

content, err := ioutil.ReadAll(resp.Body)
//  fmt.Println(string(content))
var json_resp_l []map[string]interface{}
Json(content, &json_resp_l)
if len(json_resp_l) == 0 {
    var json_resp map[string]interface{}
    Json(content, &json_resp)
    if json_resp != nil {
        json_resp_l = append(json_resp_l, json_resp)
    }
}
PrettyPrint(json_resp_l)
return json_resp_l
}

func main() {
   GetRequests("server.com", "/path")
}

答案 4 :(得分:0)

我认为关键原因是您应该为每个http.Transport使用共享http.Clienthttp.Transport将池化连接并重用它们以提高性能。

答案 5 :(得分:0)

我遇到相同的no such host错误,尽管这不是对本地主机的请求。它发生在大约250个请求之后。当然,我当然也写了defer resp.Body.Close(),但是错误不断发生。

我终于找到了一种在HTTP / 1.1请求中指定req.Header.Set("Connection", "close")的方法。这意味着连接在HTTP / 1.1中是短暂的

通常,连接后最好保持活动状态,因此不建议使用Connection: close。但是,它可能是一个有效的选项,例如,如果您要发出许多负载验证请求。