2014-10-07 5 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

어떤 버전 :

내 문제에 대한 수정 프로그램은 REQ와 고해상도의 몸을 모두 닫습니다입니까? – OneOfOne

+0

이것은 로컬 Go 설정에 문제가 있습니다. Go 버전 1.3.3도, devel + d4904f349bc8도 재생할 수 없습니다. – OneOfOne

+0

버전 1.3.3 사용 –

답변

4

신체가 닫히지 않았습니다. 서버에서 쓰기를 수행하면 응답이 아직 읽혀지지 않아 연결이 열린 채로 남아 있습니다. WriteHeader를 사용하면 응답이 완료되고 연결을 다시 사용하거나 닫을 수 있습니다.

열린 솔직한 연결로 인해 도메인 조회가 실패하는 이유를 완전히 알 수 없습니다. 250이 256에 매우 가깝다는 사실을 바탕으로, 당신이 치고있는 OS에 의해 결정되는 인공적인 한계가 있다고 생각합니다. 아마도 허용 된 최대 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입니다. 따라서 당신이 조언 한 "인공적 제한"입니다 –

+0

감사합니다. OS X가 그런 것을 알지 못했습니다. 내 생각에 최대 FD가 256으로 설정 되었다면 훨씬 현실적인 것처럼 보입니다. –

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입니다. 이것은 응용 프로그램이 응답 본문을 닫기 전에 연결이 닫히거나 풀로 반환되는 길이가 0 인 응답 본문에 대해 클라이언트에서 최적화를 트리거합니다.

0

또한 동시에 POST 요청을 할 때 MAC OSX 같은 문제를 구비하여 : 그것은 그 에러를 가질 것이다 (250 개) 요청 후

;

go1.8.3 사용이동의

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