在 fiber 中,需在 ctx.next() 前用 process.hrtime() 记录起始时间并存入 ctx.locals,next() 后取出计算耗时,避免竞态与精度误差,确保 per-request 隔离和纳秒级准确统计。

中间件里怎么拿到请求开始和结束时间
在 Fiber 中,ctx.Next() 是关键分界点:它之前是请求进入阶段,之后是响应返回阶段。中间件执行顺序是「进入 → 下游处理 → 返回」,所以必须在 ctx.Next() 前记录起始时间,在它之后读取耗时。
别用 Date.now() 或 new Date().getTime() ——它们精度低、受系统时钟调整影响。直接用 process.hrtime.bigint()(Node.js 10.7+)或 process.hrtime()(返回 [seconds, nanoseconds] 数组)更可靠。
-
process.hrtime()返回的是纳秒级差值,不受系统时间跳变影响 - 若用
bigint版本,记得除以1000000n转成毫秒(ms),避免小数精度丢失 - Fiber 的
ctx没有内置startTime属性,必须自己挂载到ctx.Locals或闭包变量中
为什么不能在 Next() 外部直接算耗时
常见错误是写成这样:
app.Use(func(c *fiber.Ctx) error {
start := time.Now()
c.Next()
log.Printf("耗时: %v", time.Since(start)) // ❌ 错!这里 start 是进入中间件的时间,不是请求真正开始处理的时间
return nil
})
问题在于:c.Next() 只是触发后续中间件和 handler,但不阻塞;如果下游用了异步操作(比如 go 协程)、或者中间件没正确 return,time.Since(start) 就会包含无关等待时间,甚至统计到其他请求的耗时。
- 必须把起始时间存进
c.Locals,并在c.Next()后从c.Locals取出再计算 - 如果 handler panic,
c.Next()后的代码不会执行——得靠defer+recover或 Fiber 的app.Use(func(c *fiber.Ctx) error { ... })的 error handling 链路兜底 - 注意:Fiber 默认不捕获 handler panic,需显式配置
app.Settings.RecoverPanics = true
ctx.Locals 和闭包变量哪个更安全
用闭包变量(比如在外层定义 var startTime time.Time)看似简单,但会因 Fiber 复用 Ctx 实例而引发竞态——多个并发请求共享同一个变量,结果完全不可控。
ctx.Locals 是每个请求独享的 map,线程安全,且生命周期与请求一致。推荐写法:
app.Use(func(c *fiber.Ctx) error {
start := process.hrtime() // 或 process.hrtime.bigint()
c.Locals("startTime", start)
c.Next()
if end, ok := c.Locals("startTime").([]int64); ok {
elapsed := int64(end[0])*1e9 + int64(end[1])
ms := float64(elapsed) / 1e6
c.Set("X-Response-Time", fmt.Sprintf("%.3fms", ms))
}
return nil
})
-
c.Locals是唯一能保证 per-request 隔离的方式 - 别往
c.Locals存函数或大对象,只放轻量数据(如时间戳、ID) - 如果要打日志,建议统一用
c.IP()+c.Path()+c.Method()+ 耗时,避免依赖全局 logger 上下文
生产环境要注意的三个细节
本地测出来没问题,上线后可能发现耗时不准、日志爆炸、或内存缓慢增长——多半栽在这几处:
- 没过滤健康检查接口(如
/health、/metrics),导致监控毛刺干扰真实业务指标 - 日志级别没控制,默认全量输出,高频接口会撑爆磁盘或日志服务(建议 >500ms 才
log.Warn,>2s 再告警) - 忘记清理
c.Locals,虽然 Fiber 会自动回收,但若中间件链路长、自定义 key 多,仍可能轻微拖慢 GC
最易被忽略的是:Fiber 的 ctx.SendStatus() 或 ctx.JSON() 等方法内部可能触发额外序列化或压缩,这部分时间也应纳入统计——所以务必把 c.Next() 放在中间件最末尾,不要提前 return。











