2014-10-07 123 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) 
    } 
}