
在 go 并发 http 请求中,若未先校验 http.response 是否为 nil 就直接调用 resp.body.close(),将触发 panic;正确做法是始终优先检查错误,再安全访问响应体。
在 go 并发 http 请求中,若未先校验 http.response 是否为 nil 就直接调用 resp.body.close(),将触发 panic;正确做法是始终优先检查错误,再安全访问响应体。
该问题本质是典型的 nil 指针解引用(nil dereference),根源在于 processAndUnmarshalResponses 函数中错误处理顺序不当:
func processAndUnmarshalResponses(resp *http.Response, err error, holder interface{}) error {
defer resp.Body.Close() // ⚠️ 危险!resp 可能为 nil
if err != nil {
return err
}
// ... 后续读取 resp.Body
}
当 http.Do() 失败(如网络超时、DNS 解析失败、连接拒绝等),resp 为 nil,而 defer resp.Body.Close() 会在函数返回前执行——此时对 nil 指针调用 .Body.Close(),立即 panic。
✅ 正确写法:永远先检查 err,再操作 resp
func processAndUnmarshalResponses(resp *http.Response, err error, holder interface{}) error {
if err != nil {
return err // 提前返回,避免后续使用 resp
}
defer resp.Body.Close() // ✅ 此时 resp 必然非 nil
// 安全读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return json.Unmarshal(body, holder)
}
此外,结合你主程序的并发场景(200+ goroutines 高频调用 GetUser),还需注意以下几点:
- HTTP 客户端复用:避免在每次请求中新建 http.Client,应全局复用并配置合理的 Timeout 和 Transport(如设置 MaxIdleConnsPerHost 防止连接耗尽);
- 错误处理不可忽略:soundcloud.GetUser(i) 返回 err 时,member 很可能为 nil,需确保业务逻辑不依赖未初始化的指针;
- 资源清理保障:仅当 resp != nil 且 err == nil 时才可安全调用 resp.Body.Close(),推荐使用 if resp != nil { defer resp.Body.Close() } 作为兜底防护(尽管正确流程下冗余,但增强健壮性)。
总结:Go 的错误处理是显式且严格的,nil 值不是异常而是常态。任何对可能为 nil 的指针解引用前,必须显式判空;尤其在 HTTP 客户端封装中,err 和 resp 的耦合关系决定了——错误检查永远是第一道防线。











