응답을 닫지 않으면 어떻게 될 수 있습니까?


101

Go에서 몇 가지 http 응답이 있으며 때때로 전화하는 것을 잊습니다.

resp.Body.Close()

이 경우 어떻게됩니까? 메모리 누수가 있습니까? 또한 defer resp.Body.Close()응답 대상을받은 직후 에 넣어도 안전한 가요?

client := http.DefaultClient
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
    return nil, err
}

어떤이 오류가 발생, 수있는 경우 resp또는 resp.Body전무 할?


resp.Body.Close () 뒤에 resp.Body.Close ()를 넣어도 괜찮습니다. 왜냐하면 err이 nil이 아니면 이미 닫혀 있기 때문입니다. 반면에 요청이 성공하면 본문을 명시 적으로 닫아야합니다.
Vasantha Ganesh K

답변:


113

이 경우 어떻게됩니까? 메모리 누수가 있습니까?

리소스 누출입니다. 연결은 재사용되지 않으며 열린 상태로 유지 될 수 있으며이 경우 파일 설명자가 해제되지 않습니다.

또한 응답 객체를 얻은 직후 defer resp.Body.Close ()를 넣어도 안전합니까?

아니오, 문서에 제공된 예를 따르고 오류를 확인한 후 즉시 닫으십시오.

client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
    return nil, err
}
defer resp.Body.Close()

로부터 http.Client문서 :

반환 된 오류가 nil이면 응답에는 사용자가 닫을 것으로 예상되는 nil이 아닌 본문이 포함됩니다. 본문이 EOF로 읽혀지고 닫혀 있지 않으면 클라이언트의 기본 RoundTripper (일반적으로 전송)가 후속 "연결 유지"요청을 위해 서버에 대한 영구 TCP 연결을 재사용하지 못할 수 있습니다.


5
링크 에 따르면 여전히 코드와의 연결을 유출 할 수 있습니다. 응답이 nil이 아니고 오류가 nil이 아닌 경우가 있습니다.
mmcdole

14
@mmcdole : 그 게시물은 틀렸고, 오류에 대해 반환되는 응답에는 정의 된 상태가 없기 때문에 당황하지 않을 것이라는 보장은 없습니다. Body가 오류로 인해 닫히지 않으면 버그이며보고해야합니다. 임의의 블로그 게시물이 아니라 "오류 발생시 모든 응답을 무시할 수 있음" 이라는 공식 클라이언트 문서를 참조 해야합니다 .
JimB

2
@ del-boy : 해당 클라이언트가 더 많은 요청을 할 것으로 예상되면 연결을 다시 사용할 수 있도록 본문을 읽어야합니다. 연결이 필요하지 않으면 본문을 읽지 마십시오. 본문을 읽으면 io.LimitReader. 요청이 너무 크면 새 연결을 만드는 것이 더 빠르기 때문에 일반적으로 상당히 작은 제한을 사용합니다.
JimB

1
이렇게 _, err := client.Do(req)하면 파일 설명자가 열린 상태로 유지 된다는 점을 지적 할 가치가 있습니다. 따라서 응답이 무엇인지 신경 쓰지 않더라도 변수에 할당하고 본문을 닫아야합니다.
j boschiero

1
관심있는 사람들을위한, 전체 문서 (강조 추가)이다. "오류에 어떤 응답 CheckRedirect이 실패하면 nil이 아닌 오류와 비 무기 호 응답에만 발생 무시 될 수 있고, 그렇다하더라도 반환 Response.Body은 이미 닫은."
nishanthshanmugham

15

만약이 Response.Body폐쇄되지 않습니다 Close()FD와 관련된 자원이 해제되지 않습니다보다 방법. 이것은 자원 누출입니다.

폐쇄 Response.Body

에서 응답 소스 :

Body를 닫는 것은 호출자의 책임입니다.

따라서 개체에 바인딩 된 종료자가 없으며 명시 적으로 닫아야합니다.

오류 처리 및 지연된 정리

오류시 모든 응답을 무시할 수 있습니다. nil이 아닌 오류가있는 nil이 아닌 응답은 CheckRedirect가 실패한 경우에만 발생하며 반환 된 Response.Body는 이미 닫혀 있습니다.

resp, err := http.Get("http://example.com/")
if err != nil {
    // Handle error if error is non-nil
}
defer resp.Body.Close() // Close body only if response non-nil

4
오류 처리 조건 내에서 반환되어야합니다. 사용자가 오류 처리로 돌아 오지 않으면 패닉이 발생할 수 있습니다.
applewood

3

처음에는 위에서 언급 한 것처럼 디스크립터가 닫히지 않습니다.

또한 golang 은 false persistConn인 경우 재사용을 위해 연결 ( 구조체를 사용 하여 래핑)을 캐시합니다 DisableKeepAlives.

사용 client.Do방법 후 golang 에서 go는 goroutine을 실행합니다.readLoop 단계 중 하나로 method를 합니다.

따라서 golang http transport.go에서는 메서드 에서 req가 취소 될 때까지 a pconn(persistConn struct)idleConn채널 에 삽입되지 않으며 req가 취소 될 때 readLoop까지이 goroutine ( readLoopmethod)도 차단됩니다.

다음은이를 보여주는 코드 입니다.

더 많은 것을 알고 싶다면 readLoop방법 을 봐야합니다 .


1

참조 https://golang.org/src/net/http/client.go를
"ERR이 전무 경우 인공 호흡기 항상 nil이 아닌 resp.Body이 들어."

그러나 그들은 err! = nil, resp가 항상 nil이라고 말하지 않습니다. 그들은 다음과 같이 말합니다.
"resp.Body가 닫히지 않으면 클라이언트의 기본 RoundTripper (일반적으로 전송)가 후속"연결 유지 "요청을 위해 서버에 대한 영구 TCP 연결을 재사용하지 못할 수 있습니다."라고 말합니다.

그래서 나는 일반적으로 다음과 같은 문제를 해결했습니다.

client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
   defer resp.Body.Close()
}
if err != nil {
    return nil, err 
}

3
이것은 올바르지 않으며 오류가있을 때 resp.Body가 nit nil이라는 보장이 없습니다.
JimB

1
감사합니다 @JimB. 문서의 문구는 "오류시 모든 응답을 무시할 수 있습니다."입니다. "오류시 응답 본문은 항상 닫혀 있습니다."라고 말하는 것이 더 정확합니다.
candita

1
아니요, 일반적으로 닫을 응답 본문이 없기 때문입니다. 문서에서 해당 단락을 계속 읽으면 "nil이 아닌 오류가있는 nil이 아닌 응답은 CheckRedirect가 실패 할 때만 발생하며 반환 된 Response.Body는 이미 닫혀 있습니다."
JimB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.