翻页接口必须单独监控,因其易触发深度分页、全表扫描或复杂JOIN,拖慢数据库和PHP进程;需用桶化标签(如p1、p2-10、l20)避免高基数导致Prometheus性能下降,并确保CollectorRegistry单例、Histogram命名唯一、observe调用加try/catch防500。

翻页接口为什么必须单独监控
翻页请求(如 GET /api/items?page=5&limit=20)往往触发深度分页、全表扫描或复杂 JOIN,比普通列表接口更容易拖慢数据库、卡住 PHP 进程。Prometheus 不会自动识别“这是翻页”,你得主动打标签区分——否则所有请求混在 webman_http_request_duration_seconds 里,p99 延迟飙升时根本看不出是第 100 页还是第 1 页拖垮的。
给翻页指标加 page 和 limit 标签要避开三个坑
用 $histogram->observe($duration, ['page' => $page, 'limit' => $limit]) 看似合理,但实际会炸:
-
page和limit是高基数标签:每页一个值,1000 页就生成 1000 条时间序列,Prometheus 存储和查询直接变慢 - 标签值不能含非法字符:
page="100"合法,page="100.5"或page=""会导致render()报 500 - 前端可能传恶意值:
page=-1、page=abc,不校验就塞进标签,Prometheus 解析失败
正确做法是做桶化(bucketing):
// 把 page 映射为区间标签
$page_bucket = match (true) {
$page <= 1 => 'p1',
$page <= 10 => 'p2-10',
$page <= 100 => 'p11-100',
default => 'p100+'
};
$limit_bucket = match ($limit) {
10 => 'l10',
20 => 'l20',
50 => 'l50',
default => 'l_other'
};
$histogram->observe($duration, [$page_bucket, $limit_bucket]);
如何避免翻页监控污染全局指标
Webman 多 Worker 下,如果每个请求都 new 一个 CollectorRegistry,翻页指标会和主流程指标错乱。关键点只有两个:
立即学习“PHP免费学习笔记(深入)”;
-
CollectorRegistry必须单例,放在app/Bootstrap.php中初始化,绑定到Container::set('prometheus.registry', $registry) - 翻页专用的
Histogram要用唯一名称注册,比如api_items_list_page_duration_seconds,别和通用http_request_duration_seconds混用
否则 Prometheus 抓到的数据会出现同名指标多实例,Grafana 查 p99 时曲线跳变、counter 重置,不是你代码写错了,是指标注册逻辑本身不可靠。
/metrics 返回空或 500?先查翻页中间件里的 observe() 调用
翻页逻辑常放在自定义中间件或 Controller 里,最容易出问题的是这里:
- 传了
null给observe():比如$duration没赋值,PHP 自动转成"0",但 Prometheus 要求数值是 float,会静默失败 - 标签数组长度和注册时声明的不一致:注册时写
['page_bucket', 'limit_bucket'],调用时只传一个,observe()直接 throw Exception - 没 catch 异常:翻页报错后,
observe()抛异常,/metrics整个挂掉,返回 500
务必在翻页逻辑中包一层 try/catch,哪怕只是记录日志,也不能让指标收集逻辑影响业务响应。
翻页监控最难的不是埋点,是标签设计和错误隔离——一旦把高基数参数当标签,或让指标注册逻辑随请求生命周期走,后续所有可视化和告警都会建立在不可靠数据上。



















