
本文介绍使用 goroutine 和 channel 实现高并发 http 请求的完整方案,解决 1500+ url 批量探测耗时过长的问题,将执行时间从 16 分钟大幅缩短,并提供可复用、可配置范围的并发函数。
本文介绍使用 goroutine 和 channel 实现高并发 http 请求的完整方案,解决 1500+ url 批量探测耗时过长的问题,将执行时间从 16 分钟大幅缩短,并提供可复用、可配置范围的并发函数。
在 Go 中实现 HTTP 请求的并发处理,核心在于合理利用 goroutine(轻量级协程)与 channel(安全通信管道),避免阻塞、竞争和资源耗尽。原始代码存在多个典型问题:全局变量 i 被多个 goroutine 共享导致竞态;未限制并发数引发连接风暴;channel 读写未做同步控制,threadPrint 会无限阻塞;且缺乏错误处理与超时机制。
以下是一个生产就绪的改进方案,封装为通用函数 getBusPostStatus(start, end int),支持任意 ID 区间并发探测,并内置连接复用、超时控制与错误抑制:
package main
import (
"fmt"
"net/http"
"time"
)
// 自定义 HTTP 客户端,设置超时防止请求挂起
var client = &http.Client{
Timeout: 5 * time.Second,
}
// getBusPostStatus 并发探测指定编号区间的公交站牌 URL 状态
// start(含)到 end(不含),例如 getBusPostStatus(600, 800) 探测 600~799 共 200 个站点
func getBusPostStatus(start, end int) <-chan string {
results := make(chan string, end-start) // 带缓冲 channel,避免 goroutine 阻塞
for i := start; i < end; i++ {
go func(id int) {
url := fmt.Sprintf("http://www.urbanosdezaragoza.es/frm_esquemaparadatime.php?poste=%d", id)
resp, err := client.Get(url)
if err != nil {
results <- fmt.Sprintf("ERROR: Bus post %d failed — %v", id, err)
return
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
results <- fmt.Sprintf("OK: Bus post %d exists", id)
} else {
results <- fmt.Sprintf("WARN: Bus post %d returned status %d", id, resp.StatusCode)
}
}(i)
}
return results
}
func main() {
startTime := time.Now()
// 启动多个并发批次(如每批 50 个,共 30 批 → 1500 个)
const total = 1500
const batchSize = 50
var allResults []string
// 收集所有结果 channel
resultChannels := make([]<-chan string, 0, total/batchSize)
for start := 0; start < total; start += batchSize {
end := start + batchSize
if end > total {
end = total
}
resultChannels = append(resultChannels, getBusPostStatus(start, end))
}
// 按批次接收并打印结果(也可统一收集后排序输出)
for _, ch := range resultChannels {
for j := 0; j < batchSize && j < total-len(allResults); j++ {
if result, ok := <-ch; ok {
allResults = append(allResults, result)
fmt.Println(result)
}
}
}
elapsed := time.Since(startTime)
fmt.Printf("\n✅ Completed %d requests in %v\n", len(allResults), elapsed)
}关键优化说明:
- ✅ 无竞态安全:每个 goroutine 使用独立参数
id,避免共享变量i; - ✅ 可控并发:通过
batchSize间接限流(如需严格限制并发数,可用semaphore或 worker pool 模式); - ✅ 健壮性增强:
http.Client设置Timeout,显式Close()响应体,错误分类输出; - ✅ 内存友好:使用带缓冲 channel(容量 = 批次大小),避免 goroutine 因发送阻塞而堆积;
- ✅ 灵活复用:
getBusPostStatus(start, end)可直接调用,如go getBusPostStatus(600, 800);
⚠️ 注意事项:
- 目标服务器可能对高频请求返回
429 Too Many Requests,建议添加time.Sleep()(如每 10 请求休眠 100ms)或使用rate.Limiter; - 生产环境应启用
http.Transport连接池(默认已启用),并根据需要调优MaxIdleConns等参数; - 若需结构化结果(如统计 OK/WARN 数量),可将
stringchannel 替换为自定义 struct channel(如chan struct{ID int; Status string; Code int})。
通过以上设计,1500 次请求通常可在数秒至一分钟内完成(取决于网络与服务端响应),真正发挥 Go 并发模型的高效优势。

















