Golang使用WaitGroup并发调用API时10000次请求耗时过长问题
问题分析与解决方案
你的WaitGroup用法完全正确,性能差异的核心原因不在WaitGroup,而是HTTP客户端的默认连接限制和大规模并发请求带来的调度/限流问题。
为什么会出现耗时飙升?
HTTP客户端默认连接限制
Go标准库net/http的默认客户端(http.DefaultClient)使用的Transport有严格的并发连接限制:MaxIdleConnsPerHost默认是2,意味着同一主机同时只能有2个空闲连接- 实际并发请求会被操作系统文件描述符、服务器端连接数限制进一步约束
当发起1000甚至10000个并发请求时,大部分请求会排队等待可用的HTTP连接,导致总耗时急剧增加。
目标服务器限流
jsonplaceholder这类公共API通常会对高频请求做速率限制,短时间内发起大量请求时,服务器可能延迟响应甚至返回错误,进一步拉长总耗时。过多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
相关产品推荐
相关产品推荐

