Go语言通过goroutine和标准库高效处理并发HTTP请求。1. 使用sync.WaitGroup控制多个goroutine同步执行;2. 通过channel限制并发数并传递结果;3. 利用context实现超时与取消;4. 使用errgroup简化错误处理与任务取消。根据场景选择合适模式可提升性能与稳定性。

在Golang中处理并发HTTP请求是构建高性能网络服务的核心能力之一。Go语言通过轻量级的goroutine和丰富的标准库支持,使得并发编程变得简单高效。以下是几种常用的并发HTTP请求处理方法,适用于不同场景下的需求。
使用Goroutine发起多个HTTP请求
最直接的方式是为每个HTTP请求启动一个独立的goroutine。这种方式适合需要同时获取多个资源的场景,比如聚合多个API数据。
注意:需配合sync.WaitGroup控制主程序等待所有请求完成。
package mainimport ( "fmt" "net/http" "sync" )
func fetch(url string, wg *sync.WaitGroup) { defer wg.Done() resp, err := http.Get(url) if err != nil { fmt.Printf("Error fetching %s: %v\n", url, err) return } defer resp.Body.Close() fmt.Printf("Fetched %s with status: %s\n", url, resp.Status) }
func main() { var wg sync.WaitGroup urls := []string{ "https://www.php.cn/link/4d2fe2e8601f7a8018594d98f28706f2", "https://www.php.cn/link/5f4d36824abbbe0b96729728d035a7ae", "https://www.php.cn/link/0ad3140ed0cf59e84008db87c8c1106c", }
for _, url := range urls { wg.Add(1) go fetch(url, &wg) } wg.Wait()}
使用Channel收集结果与控制并发数
当请求数量较大时,直接启动大量goroutine可能导致资源耗尽。通过channel可以限制并发数量,并安全地传递结果和错误。
立即学习“go语言免费学习笔记(深入)”;
func limitedConcurrentFetch(urls []string, maxConcurrency int) {
type result struct {
url string
err error
code int
}
sem := make(chan struct{}, maxConcurrency)
results := make(chan result, len(urls))
for _, url := range urls {
go func(u string) {
sem <- struct{}{} // 获取信号量
resp, err := http.Get(u)
if err != nil {
results <- result{u, err, 0}
} else {
results <- result{u, nil, resp.StatusCode}
resp.Body.Close()
}
<-sem // 释放信号量
}(url)
}
// 收集所有结果
for range urls {
res := <-results
if res.err != nil {
fmt.Printf("Failed %s: %v\n", res.url, res.err)
} else {
fmt.Printf("Success %s: %d\n", res.url, res.code)
}
}}
使用Context控制超时与取消
在并发请求中引入context能有效避免长时间阻塞。可设置整体超时或单个请求的截止时间,提升系统响应性。
func fetchWithContext(ctx context.Context, url string) error {
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
var wg sync.WaitGroup
urls := []string{"https://httpbin.org/delay/1", "https://httpbin.org/delay/3"}
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
err := fetchWithContext(ctx, u)
if err != nil {
fmt.Printf("Request failed or timed out for %s: %v\n", u, err)
} else {
fmt.Printf("Success: %s\n", u)
}
}(url)
}
wg.Wait()}
使用errgroup简化错误处理
golang.org/x/sync/errgroup 提供了更简洁的并发控制方式,自动传播第一个返回的错误并取消其他任务。
import "golang.org/x/sync/errgroup"func main() { urls := []string{ "https://www.php.cn/link/98a733901e53052474f2320d0a3a9473", "https://www.php.cn/link/874b2add857bd9bcc60635a51eb2b697", "https://www.php.cn/link/98a733901e53052474f2320d0a3a9473", }
g, ctx := errgroup.WithContext(context.Background()) for _, url := range urls { url := url g.Go(func() error { req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("received non-200 status: %d", resp.StatusCode) } fmt.Printf("OK: %s\n", url) return nil }) } if err := g.Wait(); err != nil { fmt.Printf("One request failed: %v\n", err) }}
基本上就这些常见模式。选择哪种方式取决于具体需求:简单并发可用WaitGroup,需限流用channel加缓冲池,强调错误传播和取消则推荐errgroup + context组合。合理使用这些方法能让Go程序高效稳定地处理大量HTTP请求。不复杂但容易忽略的是超时控制和资源释放,务必在生产代码中落实。










