不能在中间件中调用 runtime.NumGoroutine() 动态返回前端,因其反映的是进程级全局协程数,与单请求无关,易导致指标混淆、误告警;应改用 Prometheus Gauge 定期采集并暴露 /metrics。

中间件里调用 runtime.NumGoroutine() 会拖慢请求吗
不会明显拖慢,runtime.NumGoroutine() 是原子读取,开销极低;但**不能在每个请求的中间件里直接打日志或上报**——日志 IO 或网络调用才是瓶颈。它适合采样式暴露(比如每秒一次),不适合 per-request 实时记录。
为什么不能把 runtime.NumGoroutine() 放进中间件里动态返回给前端
因为 Fiber 的 fiber.Ctx 是从 sync.Pool 复用的,生命周期只覆盖单次 HTTP 请求;而 goroutine 数是进程级全局快照,跟当前请求无关。强行塞进中间件响应体,会导致:
- 指标含义混乱:返回值反映的是整个服务此刻的 goroutine 总数,不是该请求引发的协程数
- 误导性告警:某次请求返回 12,不代表它开了 12 个 goroutine,可能只是刚好系统里有 12 个活跃协程(含 runtime 内部的)
- 掩盖真实问题:goroutine 泄漏要看趋势和堆栈,不是单点数字
真正可落地的监控方案:暴露 /metrics + 定期更新 Gauge
用 Prometheus 指标体系才是生产环境正确做法。关键点:
- 创建
prometheus.NewGauge()实例,**不要用NewGaugeFunc()** —— 后者会在每次 scrape 时执行函数,卡住 metrics endpoint - 启一个后台 goroutine,每 2–5 秒调用一次
gauge.Set(float64(runtime.NumGoroutine())) - 注册到 HTTP 路由:
app.Get("/metrics", promhttp.Handler()) - 确保
gauge在应用启动时就prometheus.MustRegister(gauge)
这样拿到的指标可聚合、可告警、可关联其他维度(如错误率、延迟),比中间件里硬塞一个数字有用得多。
想定位泄漏?别只看数字,要查 /debug/pprof/goroutine?debug=2
当监控曲线持续上扬,立刻访问该端点(需提前导入 _ "net/http/pprof" 并监听端口)。重点扫描:
-
semacquire:说明卡在 channel receive 或 mutex 上 -
select+ 空case <-ctx.Done()缺失:goroutine 没监听取消信号 - 第三方库中未关闭的 long-polling client、logger、metrics reporter
数字只是线索,堆栈才是证据。中间件里打不出这些信息。


















