“End of File” 意味着客户端和服务器之间的连接在未按预期的情况下被中断了,导致在接收响应时没能拿到完整的数据流
出在几个方面:
-
连接池管理不当
-
HTTP Keep-Alive机制
-
服务器主动关闭连接
其中,“时不时EOF”往往和连接池的使用及Keep-Alive机制有关。Golang的http.Client在默认情况下会保持连接,重用之前的TCP连接来提高性能,这就是所谓的Keep-Alive机制
原写法如下:
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://example.com", nil)
if err != nil {
fmt.Println("Request creation failed:", err)
return
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Reading response failed:", err)
return
}
fmt.Println("Response:", string(body))
}
面这个例子是不是看起来很“正经”?但它有个问题,容易触发EOF错误。具体表现就是:在某些场景下(比如请求频繁、服务端连接超时),运行几次就能碰到。
解决方案一:设置超时和重试
// 增加了超时设置,避免死等
client := &http.Client{
Timeout: 10 * time.Second,
}
解决方案二:设置Transport,自定义连接池策略
// 自定义Transport配置 定制连接池策略 调整超时时间,规范Keep-Alive的策略
transport := &http.Transport{ DialContext: (&net.Dialer{
Timeout: 30 * time.Second, // 连接超时
KeepAlive: 30 * time.Second, // 保持连接时长
}).DialContext,
MaxIdleConns: 100, // 最大空闲连接数
IdleConnTimeout: 90 * time.Second, // 空闲连接超时
TLSHandshakeTimeout: 10 * time.Second, // TLS握手超时
ExpectContinueTimeout: 1 * time.Second, // 100-Continue状态超时
}
client := &http.Client{
Transport: transport,
}
解决方案三:使用第三方库(比如resty)
用resty来做请求,代码更简洁,且内部对连接池管理、重试机制等都有优化。如果要做HTTP请求的“高频选手”,那可以考虑切换到这个库。