使用net/http与ants协程池批量检测域名时每次运行结果不一致如何解决
问题原因分析
1. 第一段代码结果长度不一致问题
你使用[]string类型的全局变量sitelist存储结果,Go语言的slice的append操作不是并发安全的:当多个goroutine同时执行append时,会出现索引计算冲突、数据覆盖的情况,导致部分写入结果丢失,所以每次运行得到的长度都不一致。
2. 第二段代码崩溃问题
有两个核心原因:
- map并发写不安全:Go的map默认不支持并发写入,多个goroutine同时对同一个map执行赋值操作会直接触发panic,是Go语言的运行时强制检查机制。
- 并发数过高触发系统资源限制:你设置了1000的协程池并发度,瞬间发起1000个HTTP请求,首先会产生大量并发DNS查询请求,绝大多数本地DNS服务、操作系统都会对并发DNS查询做阈值限制,你贴出的报错栈就是卡在DNS解析阶段;其次1000并发也会占用大量TCP连接、文件描述符,超过系统限制后也会引发程序崩溃。
另外两段代码都存在HTTP响应Body未关闭的问题,会导致文件描述符泄漏,随着请求增多资源耗尽也会引发异常。
修复方案
你可以参考以下调整后的代码逻辑:
- 新增
sync.Mutex互斥锁保护对共享变量的写入操作 - 降低并发数到合理区间(建议50~100)
- 新增HTTP响应Body的关闭逻辑
- 补充必要的错误处理
package main import ( "bufio" "log" "net/http" "os" "sync" "time" "github.com/panjf2000/ants" ) var ( sitelist []string mu sync.Mutex // 新增互斥锁保护共享资源写入 ) func check(pool interface{}) { address := pool.(string) client := &http.Client{} client.Timeout = time.Second * 5 req, err := http.NewRequest("GET", "http://"+address, nil) if err != nil { mu.Lock() sitelist = append(sitelist, "not-works:"+address) mu.Unlock() return } response, err := client.Do(req) if err != nil { mu.Lock() sitelist = append(sitelist, "not-works:"+address) mu.Unlock() return } defer response.Body.Close() // 关闭响应Body避免资源泄漏 status := response.StatusCode mu.Lock() if status == 200 { sitelist = append(sitelist, "works:"+address) } else { sitelist = append(sitelist, "not-works:"+address) } mu.Unlock() } func main() { defer ants.Release() var wg sync.WaitGroup // 调低并发度避免资源超限 p, _ := ants.NewPoolWithFunc(100, func(i interface{}) { check(i) wg.Done() }) defer p.Release() file, err := os.Open("example.txt") if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { wg.Add(1) a := scanner.Text() _ = p.Invoke(string(a)) } wg.Wait() log.Println(len(sitelist)) }
如果要使用map存储结果,只需要在对map赋值的前后加锁即可,逻辑和上述实现一致。
内容的提问来源于stack exchange,提问作者Can
相关产品推荐
相关产品推荐

