You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go首次循环正常,二次迭代触发nil指针解引用panic求助

问题根源与解决方案

这个问题的核心和GC完全没关系,问题出在HTTP响应体res.Body的特性上——它是一个只能被读取一次的io.ReadCloser流!

你提到edgar.GetCompanyFilingListRes会消费res.Body,这意味着第一次循环时,这个函数已经把Body里的数据流读完了,甚至很可能在读取完成后调用了Body.Close()(这是处理HTTP响应的常规操作)。等到第二次循环再把同一个res传进去时,Body已经处于被关闭/读取完毕的状态,此时尝试访问它自然会触发nil pointer dereference的panic。


解决方案

根据你的场景,有两种常见的解决思路:

方案1:复用响应体内容(若能修改GetCompanyFilingListRes)

如果可以修改edgar包的GetCompanyFilingListRes函数,建议先把res.Body的内容完整读取到内存中,再基于内存数据处理,这样就能多次复用数据:

// 修改后的GetCompanyFilingListRes示例
func GetCompanyFilingListRes(res *http.Response) []map[string]interface{} {
    // 先把Body内容读入内存
    bodyBytes, err := io.ReadAll(res.Body)
    if err != nil {
        // 按需处理错误
        return nil
    }
    // 务必关闭Body释放资源
    res.Body.Close()
    
    // 后续基于bodyBytes解析数据(比如JSON反序列化等)
    // ...
    // 返回处理后的map切片
}

修改后你就可以在循环外只发起一次请求,多次复用解析后的内存数据。

方案2:每次循环重新发起请求(无法修改函数时)

如果没办法修改GetCompanyFilingListRes,最简单的办法就是把HTTP请求放到循环内部,每次迭代都获取一个全新的响应对象,这样每次都有未被消费的Body可以使用:

package main
import (
	"data/edgar"
	"fmt"
	"net/http"
)
func main() {
	url := "calltomylocalserver.com/page.html"
	for i := 0; i < 100000; i++ {
		// 每次循环重新发起请求
		res, err := http.Get(url)
		if err != nil {
			fmt.Printf("第%d次请求失败: %v\n", i, err)
			continue
		}
		m := edgar.GetCompanyFilingListRes(res)
		fmt.Println(m)
		// 手动关闭Body,避免资源泄漏
		_ = res.Body.Close()
	}
}

这里要提醒你:之前的代码忽略了http.Get的错误返回,这是很危险的编程习惯,一定要加上错误处理逻辑。


再强调一次:这个问题和GC无关——你的res变量在循环中一直被引用,GC不会回收它。问题的本质就是HTTP响应体的一次性消费特性,只要解决了Body的复用/重新获取问题,panic就会消失。

内容的提问来源于stack exchange,提问作者AppTest

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:56:39