PV统计中间件必须作为第一个app.Use()注册,以捕获所有请求(含404、OPTIONS、静态资源等),避免被后续路由或中间件拦截;需过滤健康检查路径,并用Redis持久化计数,确保口径统一。

直接用 app.Use() 注册一个计数中间件就能实现,但要注意它必须放在路由注册之前,否则统计会漏掉 404 请求。
为什么 PV 统计中间件必须放在 app.Use() 且早于所有 app.Get()/app.Post()
因为 Fiber 的中间件执行顺序严格按 app.Use() 调用顺序来。如果把统计中间件写在某个 app.Get("/api/user", ...) 后面,那它就只对这个路由生效;而写在最前面,才能捕获所有进入的请求(包括未匹配路由的 404)。PV 是“页面访问量”,不是“成功接口调用量”,所以得统计所有进来的 HTTP 请求。
- 全局 PV 统计必须是第一个
app.Use(),否则会被后续中间件(如鉴权、CORS)拦截或跳过 - 不要把它塞进某个具体路由 handler 里——那样漏掉 OPTIONS 预检、静态资源、404 等真实访问
- 如果用了
app.All("*", ...)捕获兜底,反而可能重复计数,不推荐
c.Locals() 和全局变量怎么选?别用 var count int
单机部署时看似可以用包级变量 var pvCount int64 配合 atomic.AddInt64(),但实际会出问题:Fiber 默认启用多 goroutine 处理并发请求,而包变量没有自动同步语义。更稳妥的是用 c.Locals() 做上下文标记 + 外部存储(如 Redis)做持久化计数。
- 用
c.Locals("pv_tracked", true)防止同一请求被多个中间件重复计数 - 真正累加操作建议发到 Redis:
redisClient.Incr(ctx, "pv:total"),避免内存丢失和竞态 - 如果只是开发环境快速验证,可用
sync/atomic,但上线前必须换 Redis 或 Prometheus
如何排除健康检查、监控探针等干扰流量?
像 /healthz、/metrics、/favicon.ico 这类请求不该算进 PV。最简单的方式是在中间件里加路径白名单过滤:
app.Use(func(c *fiber.Ctx) error {
path := c.Path()
if path == "/healthz" || path == "/metrics" || path == "/favicon.ico" {
return c.Next()
}
// 执行 PV 计数逻辑
return c.Next()
})
- 注意
c.Path()返回的是标准化路径(已去除重复斜杠、解析了..),比c.OriginalURL()更可靠 - 不要用正则匹配大量路径——性能差,且容易漏掉带 query 的变体(如
/healthz?from=ci) - 如果前端有埋点或网关层做了流量分类,建议把 PV 统计下沉到网关,后端中间件只做兜底
真正的难点不在代码几行,而在于「统计口径」是否和产品、数据团队对齐:是按请求计,还是去重 UV?是否包含爬虫?是否区分静态资源?这些得在加中间件前就定好,不然后期补数据清洗成本远高于改一行 app.Use()。


















