答案:使用信号量控制并发、复用http.Client、设置超时重试、通过channel收集结果,避免资源耗尽。具体包括:1. 用semaphore.NewWeighted(n)限制最大并发数;2. 全局复用client并调优Transport参数;3. 每个请求绑定context实现超时控制;4. 对5xx等临时错误进行指数退避重试;5. 使用worker goroutine从任务channel取URL,处理后将结果发送至结果channel;6. 主协程关闭任务channel后读取结果并输出,需保序时可携带索引排序。

用 Go 批量发 HTTP 请求,核心是控制并发、避免压垮服务端或耗尽本地资源。不靠盲目开 goroutine,而要结合限流、超时、重试和连接复用。
用 semaphore 控制并发数
直接启动成百上千 goroutine 容易触发系统级限制(如文件描述符耗尽)或被目标服务限流/拉黑。推荐用信号量(如 golang.org/x/sync/semaphore)限制同时活跃的请求数:
- 初始化一个带容量的信号量,比如
sem := semaphore.NewWeighted(10)表示最多 10 个并发 - 每个请求前调用
sem.Acquire(ctx, 1),成功才发起请求;结束后调用sem.Release(1) - 配合 context 可实现整体超时或取消,比如
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
复用 http.Client 和连接池
每次 new http.Client 都会创建独立的 Transport,浪费资源。正确做法是全局复用一个 client,并调优其 Transport:
- 设置
MaxIdleConns和MaxIdleConnsPerHost(建议设为 100 或更高),避免频繁建连 - 启用
KeepAlive(默认开启),并适当调大IdleConnTimeout(如 90s) - 禁用压缩可省 CPU(
DisableCompression: true),尤其响应体小且不敏感时
统一处理超时、重试与错误
批量请求中个别失败不可避免,需策略性应对:
立即学习“go语言免费学习笔记(深入)”;
- 每个 request 必须绑定 context,例如
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) - 对临时性错误(如 502/503、net.ErrTimeout、context.DeadlineExceeded)做有限重试(如 2 次),用指数退避
- 区分错误类型:DNS 失败、连接拒绝、TLS 握手失败等一般不重试;4xx 错误通常不重试;5xx 可酌情重试
结果收集与结构化输出
别用共享 map + mutex 收集结果,容易出错。推荐 channel + sync.WaitGroup 模式:
- 启动固定数量 worker goroutine,从任务 channel 拿 URL,处理完把结果(含 url、status、body、err)发到结果 channel
- 主 goroutine 关闭任务 channel 后,用 for-range 读取结果 channel,按需聚合或写入文件
- 若需保序,可在请求时带上索引号,结果结构体里存 index,最后按 index 排序输出
基本上就这些。Golang 的并发模型天然适合批量 HTTP 场景,关键在“控”不在“猛”。合理设限、复用资源、分类容错,比堆 goroutine 更稳更快。










