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

Golang使用WaitGroup并发调用API时10000次请求耗时过长问题

问题分析与解决方案

你的WaitGroup用法完全正确,性能差异的核心原因不在WaitGroup,而是HTTP客户端的默认连接限制和大规模并发请求带来的调度/限流问题。

为什么会出现耗时飙升?

  1. HTTP客户端默认连接限制
    Go标准库net/http的默认客户端(http.DefaultClient)使用的Transport有严格的并发连接限制:

    • MaxIdleConnsPerHost默认是2,意味着同一主机同时只能有2个空闲连接
    • 实际并发请求会被操作系统文件描述符、服务器端连接数限制进一步约束
      当发起1000甚至10000个并发请求时,大部分请求会排队等待可用的HTTP连接,导致总耗时急剧增加。
  2. 目标服务器限流
    jsonplaceholder这类公共API通常会对高频请求做速率限制,短时间内发起大量请求时,服务器可能延迟响应甚至返回错误,进一步拉长总耗时。

  3. 过多goroutine的调度开销
    虽然Go的goroutine轻量,但一次性创建10000个goroutine会增加调度器负担,带来额外性能损耗。

修复方案

1. 修正基础代码错误

你的import路径有误,需修正为:

import (
    "log"
    "net/http"
    "sync"
    "sync/atomic"
    "time"
)

2. 自定义HTTP客户端,调整连接参数

创建支持高并发的HTTP客户端,修改Transport的连接限制:

func createHTTPClient() *http.Client {
    transport := &http.Transport{
        MaxIdleConns:          100,
        IdleConnTimeout:       30 * time.Second,
        MaxIdleConnsPerHost:   100,
        MaxConnsPerHost:       100, // 根据实际情况调整单主机并发连接数
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
    return &http.Client{
        Transport: transport,
        Timeout:   10 * time.Second,
    }
}

3. 使用Worker Pool控制并发数

避免一次性创建大量goroutine,用固定数量的worker处理请求,既控制并发,又减少调度开销:

func makeRequest(n int, workerCount int) {
    wg := sync.WaitGroup{}
    count := atomic.Int32{}
    client := createHTTPClient()

    // 创建任务通道
    jobs := make(chan struct{}, n)
    for j := 0; j < n; j++ {
        jobs <- struct{}{}
    }
    close(jobs)

    s := time.Now()
    // 启动worker
    for w := 0; w < workerCount; w++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for range jobs {
                _, err := client.Get("https://jsonplaceholder.typicode.com/todos/1")
                if err != nil {
                    log.Printf("Error: %v", err)
                }
                count.Add(1)
            }
        }()
    }
    wg.Wait()
    log.Printf("Time Elapsed: %v for %d iterations (worker count: %d)", time.Since(s), n, workerCount)
}

func main() {
    cases := []int{1, 10, 100, 1000, 10000}
    workerCount := 100 // 可根据实际情况调整,比如100-200
    for _, c := range cases {
        makeRequest(c, workerCount)
    }
}

效果说明

调整后,1000和10000次请求的耗时会大幅降低,不同量级的请求耗时会更接近预期(主要取决于单次请求延迟和并发数的合理设置)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 09:19:52