无法在中间件中可靠检测客户端断开,必须在 handler 内监听 c.request().context().done();因 fiber 中间件不持有可取消 context,且 fasthttp 的 c.context().done() 仅响应连接关闭、不响应业务中断,触发延迟且不可靠。

无法在中间件中可靠检测客户端断开,必须在 handler 内监听 c.Request().Context().Done() —— Fiber 的中间件本身不持有可取消的 context,且 c.Context()(来自 fasthttp)只反映连接状态,不响应业务中断。
为什么中间件里不能用 c.Context().Done()
Fiber 的 c.Context() 返回的是 fasthttp 的底层 context,它只在连接真正关闭(如 TCP FIN/RST)时才触发 Done,但这个信号不可靠:
- 它不区分“客户端主动关浏览器”和“网络临时抖动”,且触发有延迟(可能几百毫秒到数秒)
- fasthttp 的 context 没有 cancel 机制,
c.Context().Done()在多数场景下根本不会 close,导致 select 永远阻塞 - 中间件执行完后 handler 才开始,此时连接可能已断,但中间件早已退出,无处响应
正确做法:在 handler 开头创建带超时的 context 并监听
检测断连的本质不是“监听事件”,而是让所有耗时操作能被及时中断。你必须在 handler 函数体第一行就构造可取消 context,并贯穿下游:
- 调用
ctx, cancel := context.WithTimeout(c.Context(), 10*time.Second)(注意:这里用c.Context()是为了继承连接生命周期,但真正起作用的是你传给 DB/HTTP 调用的ctx) - 立刻
defer cancel(),防止 goroutine 泄露 - 把
ctx显式传给所有阻塞操作:db.QueryRowContext(ctx, ...)、httpc.Do(req.WithContext(ctx))、time.Sleep替换为select { case - 不要在中间件里做这些 —— 中间件没有义务、也没有能力控制 handler 内部的 IO 生命周期
常见误判现象与对应原因
以下行为看似在“检测断开”,实则无效或危险:
-
c.Get("Connection") == "close":只是请求头字段,客户端可伪造,且与实际连接状态无关 -
c.Response().StatusCode() != 0:仅表示是否已写过响应头,不能说明客户端是否还在线 - 在中间件中启动 goroutine 监听
c.Context().Done():goroutine 可能比 handler 先结束,或因 panic 未执行 cancel,造成资源残留 - 依赖
c.IP()或c.Hostname()变化判断断连:这些值在连接存活期内固定,断连后也拿不到新值
最易被忽略的一点:Fiber 不像 Gin 那样自动把 http.Request.Context() 注入到每个 handler —— 它的 c.Context() 是 fasthttp 原生 context,**不可用于业务超时或取消**。你要自己造 context,并确保每一层 IO 调用都真正响应它。否则,哪怕客户端早已关闭标签,你的 DB 查询仍在后台跑着,协程卡死,连接池慢慢耗尽。











