API 必须设超时、限流 goroutine、避免 map[string]interface{} 序列化:http.Server 需显式配置 Read/Write/IdleTimeout;异步任务须用 semaphore 限流并传入 context;固定结构体应使用 easyjson 等静态序列化替代 json.Marshal。

不配超时、裸起 goroutine、用 map[string]interface{} 做响应体——这三件事做全了,API 就算写完了,也等于刚埋好雷。
http.Server 必须显式设超时,别信默认值
net/http 默认的 http.Server 没有任何超时控制:ReadTimeout、WriteTimeout、IdleTimeout 全为 0,意味着连接永不超时。一个卡住的请求就能让 goroutine 堆积,最终耗尽内存或文件描述符。
-
ReadTimeout控制从 accept 到读完 request body 的总时长,建议设为3 * time.Second;它能及时回收客户端断连但服务端还在等 body 的 goroutine -
WriteTimeout从WriteHeader开始计时,防 handler 内部阻塞(比如 DB 查询没设 context 超时),建议比业务最大预期耗时多留 2 秒 -
IdleTimeout管 keep-alive 连接空闲时间,设30 * time.Second可平衡 TIME_WAIT 和 fd 复用;设太短会逼客户端频繁重连,太长则连接长期挂起
别在 handler 里直接 go func(),用 semaphore 限流
写 go doSomething() 看似异步,实则是并发失控的起点:没上下文取消、没 panic 捕获、没资源复用、没数量限制。QPS 上千时,瞬间几百个 goroutine 吃光内存很常见。
- 用
golang.org/x/sync/semaphore的semaphore.NewWeighted(100)替代裸起 goroutine,Acquire(ctx, 1)+defer s.Release(1)是安全基线 - 所有异步任务必须接收
context.Context,且 handler 返回前调用cancel();goroutine 内部不能直接写 response,HTTP 连接可能已关闭 -
defer recover()放在 handler 里无效——它捕不到子 goroutine 的 panic;recover 必须包在 worker 函数最外层
JSON 序列化别碰 json.Marshal,尤其高频固定结构
json.Marshal 在 QPS 上万时会成为 CPU 瓶颈:反射开销在火焰图里扎眼,reflect.Value.Interface 占比飙升。map[string]interface{} 更是双重惩罚——类型擦除 + 反射。
立即学习“go语言免费学习笔记(深入)”;
- 对固定结构体(如
User、Order),用easyjson或ffjson生成静态MarshalJSON/UnmarshalJSON方法,性能提升 3–5 倍 - 避免
json:",omitempty"配合指针字段——每次判断是否为 nil 的开销被严重低估,尤其字段多时 - 简单响应(如
{"code":0,"msg":"ok"})直接拼[]byte或用sync.Pool复用bytes.Buffer,别 new encoder
中间件和 request body 读取必须小心复用
中间件一不小心就读光了 r.Body,下游 handler 再读就是空的。这不是 bug,是 io.ReadCloser 的行为契约。
- 别用
io.ReadAll或bytes.Buffer直接读r.Body;加保护用r.Body = http.MaxBytesReader(nil, r.Body, maxBodySize) - 真要复用 body,得先
io.Copy到bytes.Buffer,再用io.NopCloser(bytes.NewBuffer(buf))塞回r.Body - Gin 中
c.ShouldBindJSON前,确保没其他中间件提前消费过 body;否则手动重置:c.Request.Body = io.NopCloser(bytes.NewBuffer(buf))
最容易被忽略的不是“怎么写快”,而是“怎么不让慢请求拖垮整个服务”——超时控制、goroutine 生命周期、body 读取顺序,这三个点卡住,再好的业务逻辑也白搭。



















