Điều gì có thể xảy ra nếu tôi không đóng phản hồi.


98

Trong Go, tôi có một số phản hồi http và đôi khi tôi quên gọi:

resp.Body.Close()

Điều gì xảy ra trong trường hợp này? sẽ có một bộ nhớ bị rò rỉ? Ngoài ra, có an toàn để đưa vào defer resp.Body.Close()ngay sau khi nhận được đối tượng phản hồi không?

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

Điều gì sẽ xảy ra nếu có một lỗi, có thể resphoặc resp.Bodykhông?


Không sao cả khi đặt defer resp.Body.Close () sau err! = Nil khi return hiện diện bởi vì, khi err không phải là nil, nó đã bị đóng. Mặt khác, phần thân cần phải đóng một cách rõ ràng khi yêu cầu thành công.
Vasantha Ganesh K

Câu trả lời:


110

Điều gì xảy ra trong trường hợp này? sẽ có một bộ nhớ bị rò rỉ?

Đó là một sự rò rỉ tài nguyên. Kết nối sẽ không được sử dụng lại và có thể vẫn mở trong trường hợp đó trình mô tả tệp sẽ không được giải phóng.

Ngoài ra, có an toàn không khi đặt defer resp.Body.Close () ngay sau khi nhận được đối tượng phản hồi?

Không, hãy làm theo ví dụ được cung cấp trong tài liệu và đóng nó ngay sau khi kiểm tra lỗi.

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

Từ http.Clienttài liệu:

Nếu lỗi trả về là nil, thì Phản hồi sẽ chứa Nội dung không phải là nil mà người dùng dự kiến ​​sẽ đóng. Nếu Body không được đọc cho EOF và bị đóng, RoundTripper cơ bản của Client (thường là Transport) có thể không sử dụng lại được kết nối TCP liên tục với máy chủ cho yêu cầu "duy trì hoạt động" tiếp theo.


4
Theo liên kết này, vẫn có thể bị rò rỉ kết nối với mã của bạn. Có một số trường hợp trong đó phản hồi là không và lỗi là không.
mmcdole

13
@mmcdole: Bài đăng đó chỉ là sai và không có gì đảm bảo rằng nó sẽ không bị hoảng loạn, vì bất kỳ phản hồi nào được trả lại về lỗi đều không có trạng thái xác định. Nếu một Body không đóng do lỗi, thì đó là lỗi và cần được báo cáo. Bạn nên xem tài liệu khách hàng chính thức , trong đó có ghi "Nếu có lỗi, mọi Phản hồi có thể bị bỏ qua", thay vì một bài đăng blog ngẫu nhiên.
JimB

2
@ del-boy: Nếu bạn mong đợi khách hàng đó đưa ra nhiều yêu cầu hơn, thì bạn nên cố gắng đọc phần nội dung để có thể sử dụng lại kết nối. Nếu bạn không cần kết nối, thì đừng bận tâm đến việc đọc nội dung. Nếu bạn đọc phần nội dung, hãy quấn nó bằng io.LimitReader. Tôi thường sử dụng một giới hạn khá nhỏ, vì việc tạo kết nối mới sẽ nhanh hơn nếu yêu cầu quá lớn.
JimB

1
Cần phải chỉ ra rằng việc làm này _, err := client.Do(req)cũng dẫn đến việc bộ mô tả tệp luôn mở. Vì vậy, ngay cả khi người ta không quan tâm đến phản hồi là gì, thì vẫn cần phải gán nó cho một biến và đóng phần thân.
j boschiero

1
Đối với bất kỳ ai quan tâm, tài liệu đầy đủ là (nhấn mạnh thêm): "Khi có lỗi, bất kỳ Phản hồi nào cũng có thể bị bỏ qua. Một Phản hồi không nil có lỗi không phải chỉ xảy ra khi CheckRedirect không thành công và thậm chí sau đó Phản hồi được trả về. đã đóng cửa. "
nishanthshanmugham

15

Nếu Response.Bodykhông được đóng với Close()phương thức hơn là một tài nguyên được liên kết với fd sẽ không được giải phóng. Đây là một sự rò rỉ tài nguyên.

Đóng cửa Response.Body

Từ nguồn phản hồi :

Người gọi có trách nhiệm đóng Body.

Vì vậy, không có trình hoàn thiện nào bị ràng buộc với đối tượng và nó phải được đóng một cách rõ ràng.

Xử lý lỗi và trì hoãn dọn dẹp

Khi có lỗi, mọi Phản hồi có thể bị bỏ qua. Phản hồi không nil với lỗi không nil chỉ xảy ra khi CheckRedirect không thành công và thậm chí sau đó Phản hồi được trả về.Body đã bị đóng.

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
Bạn nên lưu ý rằng chúng sẽ trở lại trong điều kiện xử lý lỗi của bạn. Điều này sẽ gây ra sự hoảng sợ nếu người dùng không quay lại xử lý lỗi của họ.
applewood

3

Lúc đầu, bộ mô tả không bao giờ đóng, như những thứ đã đề cập ở trên.

Và hơn thế nữa, golang sẽ lưu kết nối vào bộ nhớ cache (sử dụng persistConnstruct để bọc) để sử dụng lại nó, nếu DisableKeepAliveslà sai.

Trong golang sau khi sử dụng client.Dophương thức, go sẽ chạy readLoopphương thức goroutine như một bước.

Vì vậy, trong golang http transport.go, a pconn(persistConn struct)sẽ không được đưa vào idleConnkênh cho đến khi yêu cầu bị hủy trong readLoopphương thức và cũng như quy trình ( readLoopphương thức) này sẽ bị chặn cho đến khi yêu cầu bị hủy.

Đây là mã hiển thị nó.

Muốn biết thêm thì cần xem readLoopphương pháp.


1

Xem https://golang.org/src/net/http/client.go
"Khi err là nil, resp luôn luôn chứa non-nil resp.Body."

nhưng họ không nói khi nào err! = nil, resp luôn luôn là nil. Họ tiếp tục nói:
"Nếu resp.Body không bị đóng, RoundTripper cơ bản của Khách hàng (thường là Giao thông vận tải) có thể không sử dụng lại được kết nối TCP liên tục với máy chủ cho yêu cầu" duy trì hoạt động "tiếp theo."

Vì vậy, tôi thường giải quyết vấn đề như sau:

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

3
Điều này là không chính xác và không có gì đảm bảo rằng resp.Body là nit nil khi có lỗi.
JimB

1
Cảm ơn @JimB. Từ ngữ trong tài liệu là "Do lỗi, mọi Phản hồi có thể bị bỏ qua." Sẽ chính xác hơn nếu nói "Khi có lỗi, Phần nội dung phản hồi luôn đóng."
candita

1
Không, vì thường không có cơ quan phản hồi nào được đóng lại. Nếu bạn tiếp tục đọc đoạn văn đó trong tài liệu - "Phản hồi không phải nil với lỗi không phải nil chỉ xảy ra khi CheckRedirect không thành công và ngay cả khi Phản hồi được trả về.Body đã bị đóng."
JimB
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.