2014-10-07 114 views
2

当转到250个连接,我有以下的HTTP客户端/服务器代码:“本地主机:没有这样的主机” 后使用ResponseWriter.Write

服务器

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连接后,我得到以下错误fr om client.Do:
error: Get http://localhost:5008/250: dial tcp: lookup localhost: no such host并且没有更多的连接会成功。

如果我从w.Write([]byte("OK")) ==>w.WriteHeader(200)更改服务器的线路,但是连接数量没有限制,并且按预期工作。

我在这里错过了什么?

+0

围棋什么版本的? – OneOfOne 2014-10-07 03:39:26

+0

这是您本地Go设置的问题,我无法使用Go版本1.3.3和devel + d4904f349bc8来重现。 – OneOfOne 2014-10-07 03:43:22

+0

使用版本1.3.3 – 2014-10-07 03:44:42

回答

4

你没有关闭身体。当您从服务器执行任何写入操作时,连接保持打开状态,因为响应尚未读取。当你只是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) 
    } 
} 
+0

Mac os x - >打开的文件描述符的默认限制是256.所以这是你创建的“人为限制” – 2017-07-27 06:52:06

+0

谢谢,不知道OS X是否做到了这一点。我的猜测是,最大FD设置为256,现在看起来更为现实。 – 2017-07-27 15:28:03

5

应用程序必须关闭客户端的响应正文,如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"))时,响应正文长度为零。这将触发客户端中针对连接关闭或在应用程序关闭响应主体之前返回到池的零长度响应主体的优化。

0

另外使用做交请求时同时具有同样的问题MAC OSX:

后250个请求它将具有错误;

使用go1.8.3。

我的问题的解决方法是关闭两个REQ和RES体:

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) 
    } 
}