微服务中批量任务需避免直接用go func()启动协程,应配置http.transport的maxidleconnsperhost≥期望并发数、设置idleconntimeout和context超时、禁用全局http.client、worker内recover捕获panic、channel缓冲区宜小不宜大。

别用 go func() {} 直接起协程处理批量任务——微服务里一上来就开几万 goroutine,不是高并发,是自爆前奏。
为什么默认 http.Transport 会让协程池失效
你设了 50 个 worker,但所有 HTTP 请求卡在连接池排队,实际并发永远 ≤2。这是因为 http.DefaultTransport.MaxIdleConnsPerHost 默认是 2,不是你池子大小说了算。
- 必须显式配置
http.Transport:把MaxIdleConnsPerHost设为 ≥ 你期望的并发数(比如池大小是 20,这里至少配 20) - 顺手加上
IdleConnTimeout: 30 * time.Second,防连接堆积 - 每个请求必须套
context.WithTimeout(ctx, 30*time.Second),否则一个卡死请求拖垮整个 worker - 别复用全局
http.Client——它没超时、没 Transport 配置,等同裸奔
worker 内 panic 不 recover 就等于静默丢任务
一个未捕获的 panic 让 worker 直接退出,channel 里后续任务没人消费,池子“悄悄缩水”。线上查不到错误日志,只看到任务越跑越慢、积压越来越多。
-
recover必须包在 worker 的for range循环体内部,不是函数外层 - 写成
defer func() { if r := recover(); r != nil { log.Printf("worker panic: %v", r) } }() - 别指望上层 defer 或中间件兜底——worker 是独立 goroutine,panic 只影响自己
任务 channel 缓冲区大小不是性能指标,是风险开关
设 make(chan Task, 10000) 看似能扛住爆发,实则掩盖 I/O 卡顿,内存涨到 OOM 才暴露问题。
- 推荐小缓冲:
make(chan Task, 16)或32,够撑瞬时 burst,又不至于藏住瓶颈 - 生产侧别直接
tasks ,改用 <code>select { case tasks 防阻塞 - 关闭 channel 前必须确保所有任务已发完——
filepath.WalkDir后再close(tasks),别在遍历回调里 close
池大小不是越大越好,得看真实瓶颈在哪
SSD 上解析日志文件,8 个 worker 跑满 CPU;机械盘上开 4 个都可能因磁头寻道变慢。盲目设 100 并发,反而让调度器和 GC 成最大瓶颈。
- HTTP 下载类任务:从 5–10 起步,配合目标站限速实测调优
- CPU 密集型(如 JSON 解析、SHA256):池大小 ≈
runtime.NumCPU(),SSD 上别超 16 - 文件 I/O 类(如读大文件):优先考虑分片+单 worker 流式处理,不是堆 worker 数
- 别把
filepath.WalkDir和协程池混用——磁盘顺序读被并发打乱,实测更慢
最常被忽略的点:worker 退出前必须调 wg.Done(),否则 wg.Wait() 永远不返回,服务无法优雅停机;而这个调用很容易被 recover 或超时逻辑绕过。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











