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

Goroutine性能对比:两种HTTP请求实现哪种更高效?

问题分析与解答

两种实现的性能对比

第一种实现的性能明显更优,原因如下:

  • HTTP请求属于IO密集型操作,大部分时间都消耗在等待服务器响应、网络传输的空闲阶段。第一种方案启动5个goroutine并发执行请求,能充分利用这些等待时间,同时处理多个请求,整体耗时接近单个请求的处理时间(仅忽略微小的goroutine调度和连接建立开销)。
  • 第二种方案是在单个goroutine里串行执行5次请求,总耗时等于5个请求的耗时之和,效率远低于并发方案。

先修正doRequest函数的关键问题

你的基础请求函数存在两个严重影响性能的错误,必须先修复:

func doRequest(method string, url string, body io.Reader) (*http.Response, error) {
    // 不要忽略NewRequest的错误
    request, err := http.NewRequest(method, url, body)
    if err != nil {
        return nil, err
    }
    // request.Close需要在Do之前设置,否则不会生效
    request.Close = true
    response, err := c.httpClient.Do(request)
    // 禁止每次请求后调用CloseIdleConnections,这会彻底破坏HTTP连接池的复用机制
    // 该方法只应在客户端不再使用时调用(比如程序退出前)
    // c.httpClient.CloseIdleConnections()
    return response, err
}

错误说明:

  • request.Close = true设置时机错误:必须在调用Do()前设置,否则请求已经发送,该配置不会生效。
  • 频繁调用CloseIdleConnections():会强制关闭所有空闲的连接池连接,导致每次请求都要重新建立TCP连接,大幅增加延迟和资源消耗。

更优的实现方案

1. 优雅的并发请求管理(带错误处理)

使用errgroup可以更简洁地管理goroutine并发和错误捕获:

import "golang.org/x/sync/errgroup"

func test() error {
    g := new(errgroup.Group)
    requestCount := 5

    for i := 0; i < requestCount; i++ {
        g.Go(func() error {
            resp, err := doRequest(http.MethodGet, "http://test.com", nil)
            if err != nil {
                return err
            }
            // 必须关闭响应体,避免资源泄漏
            defer resp.Body.Close()
            // 这里可以添加响应内容的处理逻辑
            return nil
        })
    }
    // 等待所有goroutine完成,返回第一个错误
    return g.Wait()
}

2. 大请求量场景:限制并发数的Worker Pool

如果需要处理大量请求,直接启动过多goroutine会占用过多系统资源,此时可以用Worker Pool限制并发数:

import "sync"

func test() {
    var wg sync.WaitGroup
    totalRequests := 5
    maxConcurrency := 2 // 根据服务器承受能力和网络情况调整

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

    // 启动指定数量的Worker
    for i := 0; i < maxConcurrency; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for range tasks {
                resp, err := doRequest(http.MethodGet, "http://test.com", nil)
                if err == nil {
                    defer resp.Body.Close()
                }
                // 这里可以添加错误重试、日志记录等逻辑
            }
        }()
    }
    wg.Wait()
}

内容的提问来源于stack exchange,提问作者ANDERSON MANOEL PATRIGNANI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:01:24