fiber 不自动处理请求超时,需手动创建带超时的 context.context 并显式传递至所有阻塞操作;c.context() 仅响应连接关闭,不响应业务超时,故不可用于超时控制。

context.Context,并在关键路径上显式监听取消信号,否则超时只是“看起来生效”,实际协程仍在跑、连接被占、服务逐渐假死。
为什么 c.Context() 不能直接用于超时控制
c.Context() 返回的是 fasthttp 自带的底层 context,它只反映连接是否关闭(比如客户端断开),**不响应你设置的业务超时**。调用 c.Context().Done() 永远不会因 context.WithTimeout 触发,只会等 TCP 层断连——这可能几秒甚至几分钟后才发生。
正确做法是:完全忽略 c.Context() 的 cancel 能力,自己新建一个带超时的子 context。
在 handler 中正确创建并使用超时 context
所有超时逻辑必须从 handler 入口开始,且需贯穿到每一个可能阻塞的操作中。
- 在 handler 开头调用
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second),别用c.Context()作父 context - 立刻写
defer cancel(),防止 goroutine 泄露(哪怕 handler 提前 return) - 把
ctx显式传给下游:如db.QueryRowContext(ctx, ...)、httpc.Do(req.WithContext(ctx))、time.Sleep(time.Until(...).WithContext(ctx)) - 对纯计算或自定义循环,用
select监听:case
常见失效写法与对应修复
这些写法看似加了 timeout,实际完全无效:
-
错误:在 goroutine 内部才调用
context.WithTimeout→ 超时只 kill goroutine,主 handler 仍卡住,连接无法释放
修复:超时 context 必须在 handler 主流程创建,且所有阻塞调用都基于它 -
错误:调用
db.QueryRow(...)而非db.QueryRowContext(ctx, ...)→ DB 驱动根本不读取你的ctx,timeout 形同虚设
修复:确认所用 driver 支持 context(如github.com/lib/pq、github.com/go-sql-driver/mysql均支持) -
错误:用
time.AfterFunc触发c.SendString→ 发送时c可能已被复用或底层连接已关闭,导致 panic 或静默失败
修复:发送必须在select中与ctx.Done()同步判断,且优先检查ctx.Err()
中间件里做统一超时注入不可行
Fiber 的中间件链(c.Next())不感知 context 生命周期,context.WithTimeout 创建的 ctx 无法自动透传到后续 handler。你不能指望一个中间件“包住”所有路由并统一超时——因为 handler 里拿不到那个 ctx,也没法强制它用。
真正可行的方案只有两个:
• 每个 handler 自己创建和管理超时 context(最常用、最可控)
• 封装一个带超时参数的 handler 工厂函数,例如 TimeoutHandler(timeout time.Duration, fn fiber.Handler),内部完成 WithTimeout + defer cancel + ctx 透传,再注册到路由
time.After 就完事;它是一条贯穿 handler 全链路的契约——从入口创建、到每个 IO 调用传入、再到最后响应前校验。漏掉任意一环,超时就形同虚设。











