本文详解 go 程序批量请求 hacker news api 时出现 `eof` json 解析错误的根本原因(http 连接复用异常),并提供两种可靠解决方案:手动设置 `req.close = true` 或配置自定义 `http.transport` 禁用长连接。
在 Go 中批量请求多个 HTTP 接口(如 Hacker News 的 item API)时,若直接使用 http.Get() 循环调用,常会遇到看似随机的 EOF 错误,例如 json: EOF 或 unexpected end of JSON input。这并非数据本身损坏——单独访问对应 URL 完全正常,问题根源在于 服务端未按规范关闭 HTTP/1.1 持久连接。
Hacker News 的 API 服务器在返回响应后会主动关闭 TCP 连接,但未发送标准的 Connection: close 响应头。Go 的默认 HTTP 客户端(基于 http.DefaultClient)遵循 HTTP/1.1 协议,默认启用连接复用(keep-alive)。当它尝试复用一个已被服务端静默关闭的连接发起下一次请求时,底层读取将立即返回 EOF,导致 ioutil.ReadAll 获取空或截断的响应体,最终 JSON 解析失败。
✅ 正确解决方案
方案一:为每个请求显式关闭连接(推荐用于简单场景)
修改 get() 函数,不使用 http.Get(),而是构造 *http.Request 并设置 req.Close = true:
func get(url string) ([]byte, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Close = true // 强制本次请求后关闭连接
client := http.DefaultClient
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body) // 注意:ioutil 已弃用,使用 io.ReadAll
if err != nil {
return nil, err
}
return body, nil
}
? 提示:ioutil 在 Go 1.16+ 中已弃用,请改用 io.ReadAll(需导入 “io” 包)。
方案二:全局禁用 Keep-Alive(适合高并发、统一管理场景)
创建自定义 http.Client,配置 Transport 禁用连接复用,避免逐个设置: