goroutine泄漏是高并发系统最隐蔽的崩溃源,因未控制生命周期的goroutine会持续堆积,最终耗尽内存或触发gc频繁停顿;必须用context.withcancel/withtimeout传入并配合select监听done,避免嵌套启动、阻塞操作裸写及资源未释放。

goroutine 泄漏是高并发系统最隐蔽的崩溃源
不控制生命周期的 goroutine 会持续堆积,最终耗尽内存或触发 GC 频繁停顿。这不是“并发多就慢”,而是“没退出就炸”。
-
context.WithCancel或context.WithTimeout必须作为 goroutine 入口参数传入,不能只在顶层用 - 所有阻塞操作(
http.Client.Do、db.QueryRow、time.Sleep)必须包裹在select+ctx.Done()中 - 避免在 goroutine 内部直接启动新 goroutine 而不传递 context —— 容易形成“goroutine 嵌套黑洞”
- 用
go run -race检测数据竞争,但竞态检测器无法发现泄漏;需靠pprof/goroutine实时抓取堆栈确认是否堆积
限流不能只靠 middleware,得嵌入关键路径
HTTP 中间件限流(如 Gin 的 gin-contrib/limiter)只能拦住请求入口,但下游 DB 查询、RPC 调用、消息发送等环节仍可能被击穿。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 对数据库连接池设硬上限:
db.SetMaxOpenConns(20)+db.SetMaxIdleConns(10),避免连接数雪崩 - 远程调用必须配
context.WithTimeout和独立限流器,比如用golang.org/x/time/rate.Limiter控制每秒最大调用次数 - 分布式场景下,单机限流无效 —— 必须用 Redis + Lua 实现原子计数,否则多实例会突破总量阈值
- 不要把限流逻辑写死在 handler 里;建议封装成可注入的
RateLimiter接口,便于测试和替换策略
熔断器不是“开关”,而是状态机,必须配半开探测
很多团队把熔断当成简单开关:错误率超 50% 就关,等 30 秒再开。结果要么永远卡在“开”状态反复失败,要么刚恢复就打满下游。
- 选库别只看 star 数 ——
sony/gobreaker支持自定义状态转换条件,hystrix-go已停止维护 - 熔断窗口至少设为 10 秒以上,太短会导致抖动误判;半开状态必须限制试探请求数(如 3 个),且只允许成功才关闭熔断
- 不同依赖要单独配置熔断器:DB 查询和第三方 API 的错误特征完全不同,共用一个熔断器会互相污染
- 熔断日志必须记录
state、failures、requests三元组,否则无法回溯为何触发
sync.Pool 不是万能缓存,滥用反而增加 GC 压力
看到别人用 sync.Pool 提升性能,就给所有 struct 都套一层 —— 这是典型误用。它只适合生命周期短、创建开销大的对象。
- 适用场景:HTTP body 缓冲区、JSON 解析器、protobuf message 实例;不适用:DB 连接、长生命周期 service 实例、含指针的复杂结构体
- Pool 的 Get/put 必须成对出现,且不能跨 goroutine 使用同一个对象 —— 否则会引发数据错乱
- 如果对象内含
sync.Mutex或其他非零值字段,Put 前必须手动重置,否则下次 Get 可能拿到已加锁或脏数据的状态 - 压测时观察
/debug/pprof/heap,若 Pool 对象占比持续上升,说明没被及时回收,需检查 Put 是否遗漏
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










