Golang大量并发请求时出现net/http: request canceled错误排查
解决Go并发请求中出现"net/http: request canceled"错误的问题
你遇到的这个错误核心原因是复用了同一个http.Request实例在多个goroutine中并发调用。
http.Request对象并非并发安全的,当多个goroutine同时执行client.Do(req)时,会并发修改该请求对象的内部状态(哪怕你的请求Body为nil,底层仍有一些共享字段会被并发读写),这种并发冲突会直接导致请求被异常取消,从而抛出net/http: request canceled错误。这也解释了为什么错误会在程序启动后立即出现——本质是客户端代码的并发安全问题,而非服务端或超时问题。
修复方案
你需要确保每个goroutine使用独立的http.Request实例,有两种常见方式:
- 在每个goroutine内部创建新的请求
把创建请求的逻辑移到goroutine函数内部,保证每个协程拥有独立的请求对象:
package main import ( "crypto/tls" "fmt" "net" "net/http" "sync" "sync/atomic" "time" ) func main() { client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, DialContext: (&net.Dialer{ Timeout: 3 * time.Second, KeepAlive: 10 * time.Second, DualStack: true, }).DialContext, }, } reqCntr := uint32(0) wg := sync.WaitGroup{} now := time.Now() for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() // 每个goroutine创建独立的请求实例 req, err := http.NewRequest(http.MethodGet, "https://somedomain.com", nil) if err != nil { fmt.Printf("failed to create request: %v\n", err) return } resp, err := client.Do(req) if err != nil { fmt.Printf("failed to perform request %v\n", err) return } defer resp.Body.Close() // 用defer确保Body资源被释放 if resp.StatusCode != http.StatusOK { fmt.Printf("BAD STATUS CODE: %v\n", resp.StatusCode) return } atomic.AddUint32(&reqCntr, 1) }() } wg.Wait() since := time.Since(now) fmt.Printf("Performed %v requests, %v per second\n", reqCntr, reqCntr/uint32(since.Seconds())) }
- 克隆原请求对象
如果原请求有复杂的Header或其他配置,可以使用req.Clone()方法在每个goroutine中创建请求副本:
go func() { defer wg.Done() // 克隆原请求,每个goroutine使用独立副本 reqClone := req.Clone(context.Background()) resp, err := client.Do(reqClone) // 后续逻辑同上... }()
补充说明
原代码中resp.Body.Close()没有用defer包裹,可能会导致资源泄漏,修复后的代码中已经补充了这个最佳实践。
内容的提问来源于stack exchange,提问作者toozyfuzzy
相关产品推荐
相关产品推荐

