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

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实例,有两种常见方式:

  1. 在每个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()))
}
  1. 克隆原请求对象
    如果原请求有复杂的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:05:15