扇出/扇入模式需用context控制超时、waitgroup同步goroutine、带缓冲channel防阻塞、单点close防panic,并通过工作池和复用http client防止oom。

微服务里做复杂查询,别硬扛着串行调用多个下游服务——直接上并发,但得控制好节奏、超时和错误传播,否则一个慢接口拖垮整条链路。
扇出/扇入模式怎么写才不翻车
典型场景是聚合服务要同时查用户、订单、商品三个服务的数据。常见错误是开了 goroutine 就不管了,没设超时、没等结果、没关 channel,最后 goroutine 泄露或 panic。
- 必须用
context.WithTimeout给每个下游调用套一层上下文,比如ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second),调用完立刻cancel() - 每个 goroutine 写结果前先判断
ctx.Err() != nil,避免往已关闭的 channel 发数据 - 用
sync.WaitGroup等所有 goroutine 结束,再close(resultChan);别用for range resultChan直接读,容易死锁 - 结果 channel 建议带缓冲(如
make(chan Result, 3)),防止某个 goroutine 因发送阻塞而卡住
goroutine 数量失控导致 OOM 怎么防
不是“越多越快”。在高 QPS 场景下,无节制起 goroutine 会快速耗尽内存和调度器资源,尤其当每个 goroutine 都开 DB 连接或 HTTP client 时。
- 对下游调用频次高、耗时稳定的服务(如配置中心、缓存),用固定数量的工作池,比如
workerCount = runtime.NumCPU() - 任务 channel 用带缓冲的(
make(chan Task, 100)),避免生产者被压垮;缓冲大小按平均 QPS × 平均处理延时预估 - 主 goroutine 向任务 channel 发送时,加
select+default分支,防止阻塞:如果 channel 满了就丢弃或降级,别死等 - HTTP client 必须复用,禁用
http.DefaultClient,自己配Transport的MaxIdleConns和MaxIdleConnsPerHost
channel 关闭时机不对引发 panic
close() 调用过早(比如在第一个 goroutine 完成后就关)会导致其他 goroutine 往已关闭 channel 发送时 panic;关太晚又会让消费者永远阻塞。
- 只允许一个 goroutine 负责
close(),通常是主 goroutine 在WaitGroup完成后执行 - 消费者侧用
for result := range resultChan安全读取,它会在 channel 关闭且无数据时自动退出 - 别在 goroutine 里用
defer close(ch)—— defer 是函数退出时执行,但 goroutine 可能早于主流程结束,导致提前关 channel - 如果某下游失败,不要提前关 channel;用
nil或带 error 字段的 struct 传回,让消费者自己判断是否可继续
真正难的不是并发本身,而是让所有 goroutine 在超时、错误、取消信号到来时,都能干净退出、释放资源、不泄露连接——这需要每个环节都显式处理 ctx 和 channel 生命周期,而不是依赖“应该没问题”的侥幸。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











