应为下游调用(如http、db、rpc)显式使用fiber.ctx.context()设超时,而非全局请求超时;需配置http.client全链路超时参数并配defer recover()与连接池隔离。

用 ctx.Context() 包裹下游调用,不是给整个 HTTP 请求设超时
Fiber 本身不提供“全局请求超时中间件”,因为那会误杀长耗时但合法的上传、导出类请求。真正要控制的,是 你代码里发起的下游调用——比如 http.Client.Do()、数据库查询、RPC 调用。这些才是必须加超时的环节。
常见错误是试图在 app.Use() 里用 time.AfterFunc 或 context.WithTimeout(app.Settings().GetContext()) 去中断整个 fiber.Ctx 生命周期,这既不可靠(Fiber 不监听 context 取消),也违反了 Go 的上下文传递原则。
- 所有下游调用必须显式接收一个
context.Context,并传给底层 client(如http.Client) -
fiber.Ctx.Context()是 Fiber 自动注入的请求上下文,可直接用于下游调用,无需手动创建新 context - 不要用
c.Locals或自定义字段模拟超时控制,它无法触发底层 I/O 中断
http.Client 必须配 Timeout 和 Transport 级超时参数
只给 http.Request 加 context.WithTimeout 不够——如果 DNS 解析卡住、TCP 连接 hang 住、TLS 握手失败,context 无法强制中断底层 socket。必须配置 http.Client 的全链路超时:
client := &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 3 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
-
Timeout是总超时,覆盖从 DNS → 连接 → TLS → 发送 → 接收全过程 -
DialContext.Timeout控制建连阶段,避免 DNS 拖慢整体响应 - 别漏掉
TLSHandshakeTimeout,尤其在高延迟或弱证书链环境下极易卡住 - 若用
fasthttp.Client(Fiber 底层常用),对应字段是ReadTimeout、WriteTimeout、DialTimeout
协程内 panic + 超时取消必须配 defer recover() 和资源清理
超时触发后,context.Context.Done() 会关闭,但 goroutine 不会自动退出——它可能还在执行阻塞 I/O、持有锁、占着连接池。更危险的是:如果超时前发生了 panic,且没 recover,整个 goroutine 就静默死亡,连接/文件句柄/DB 连接可能泄露。
正确写法是把下游调用包进独立 goroutine,并统一处理 cancel 和 panic:
<pre class="brush:php;toolbar:false;">func callExternalAPI(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(c.Context(), 4*time.Second)
defer cancel()
<pre class="brush:php;toolbar:false;"><code>resultCh := make(chan string, 1)
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in external call: %v", r)
}
}()
// 实际调用
resp, err := client.Get(ctx, "https://api.example.com/data")
if err != nil {
resultCh <- ""
return
}
resultCh <- string(resp.Body())
}()
select {
case result := <-resultCh:
return c.SendString(result)
case <-ctx.Done():
return c.Status(fiber.StatusGatewayTimeout).SendString("upstream timeout")
}</code>}
- 每个外调 goroutine 都要
defer recover(),否则 panic 会终止 goroutine 且不通知父逻辑 -
ctx.Done()触发后,必须确保cancel()被调用,否则 context 泄露 - 别在 goroutine 内直接调用
c.SendXXX(),Fiber 的Ctx不是 goroutine 安全的
别忽略 fasthttp 的底层行为:超时后连接不会立即释放
Fiber 基于 fasthttp,而 fasthttp.Client 默认复用连接。当一次请求因超时被 cancel,底层 TCP 连接可能仍处于 ESTABLISHED 状态,直到对方主动 FIN 或本端心跳超时。这会导致连接池缓慢耗尽,尤其在短连接高频调用场景下。
解决办法只有两个:
- 在
fasthttp.HostClient中设置MaxIdleConnDuration: 30 * time.Second,强制复用连接定期断开 - 对关键下游服务,使用独立
fasthttp.HostClient实例(而非共享默认 client),避免一个慢服务拖垮全部连接池 - 监控
fasthttp.Client.OpenConns和HostClient.IdleConns指标,超时率升高时大概率伴随连接堆积
超时不是加个 WithTimeout 就完事——它是一整套协作机制:上层 context 通知、中层 client 响应、底层连接管理、协程异常兜底,缺一不可。最容易被跳过的,是 recover() 和连接池隔离这两步。











