用prometheus-cpp上报qps需使用counter类型指标(如my_requests_total)在每次请求完成时调用increment(),由prometheus端通过rate(my_requests_total[1m])计算qps;延迟则用自定义毫秒级分桶的histogram,配合histogram_quantile()查询p95/p99。

如何用 prometheus-cpp 上报 QPS(每秒请求数)
QPS 不是直接上报的指标,而是通过 Counter 累计请求数,再由 Prometheus 服务端用 rate() 函数计算得出。关键在于:必须用 Counter(不能用 Gauge),且上报频率要稳定(比如每次请求处理完立刻 Increment())。
常见错误是把 QPS 当成瞬时值塞进 Gauge,结果 rate 计算失效,图表全为 0 或跳变异常。
-
Counter必须在程序启动时注册一次,全局单例,避免重复注册导致 metrics endpoint 返回重复指标 - 每次 HTTP 请求/业务入口完成时调用
counter->Increment(),不要批量累加(否则丢失时间分布) - 确保 Prometheus 抓取间隔(
scrape_interval) ≤ 15s,否则rate(my_requests_total[1m])可能因样本不足返回空
示例:
auto counter = builder.AddCounter("my_requests_total", "Total number of requests");
// 在请求处理函数末尾:
counter->Increment();
如何上报延迟(latency)并支持 P95/P99 聚合
延迟必须用 Histogram,不是 Summary —— 前者服务端可聚合(多个实例的延迟合并计算 P95),后者只能单实例计算,不适用于多进程/多副本部署。
容易踩的坑是盲目用默认分桶(DefaultBuckets()),它们针对秒级延迟设计,而多数服务延迟在毫秒级,会导致 99% 的样本挤在第一个桶里,P95 完全不准。
- 手动设置毫秒级分桶,例如:
{0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0}(单位:秒) - 调用
histogram->Observe(duration.count() / 1000.0),注意把std::chrono::nanoseconds转成秒(count()是纳秒数,除以 1e9;若用milliseconds则除以 1e3) - 避免在异步回调中多次
Observe()同一个请求——延迟应只记录“端到端处理完成时刻”
Prometheus 查询 P95 延迟写法:histogram_quantile(0.95, rate(my_request_duration_seconds_bucket[1h]))
暴露 metrics endpoint 需要哪些最小依赖和配置
prometheus-cpp 分 client 和 pull(HTTP server)两部分。只上报不暴露 endpoint?不行——C++ client 必须配合内置 HTTP server 才能被 Prometheus 抓取。这意味着你得链接 libprometheus-cpp-pull.a 并启动一个监听线程。
- 必须链接
-lprometheus-cpp-pull -lprometheus-cpp-core,漏掉-pull会链接失败(undefined reference to `prometheus::detail::WebServer::WebServer`) - 启动 server 前需先创建
Registry并注入所有Counter/Histogram,顺序不能反 - 监听地址建议用
127.0.0.1:9091,不要绑定0.0.0.0(生产环境暴露 metrics 是安全风险) - 如果已有主 HTTP 框架(如 drogon、cpp-httplib),无法直接复用其路由,因为 prometheus-cpp pull 模块自带 server,需单独开端口或改用 pushgateway(不推荐,丢数据)
启动示例:
auto registry = std::make_shared<:registry>();
auto& family = prometheus::BuildHistogram()
.Name("my_request_duration_seconds")
.Help("Latency of my requests.")
.Labels({{"service", "api"}})
.Register(*registry);
auto histogram = family.Add({});
// 启动 server
prometheus::Exposer exposer{"127.0.0.1:9091"};
exposer.RegisterCollectable(registry);
</:registry>
为什么本地测试能看到指标,上线后 Prometheus 抓不到
最常见原因是容器或 systemd 服务没开放对应端口,或者防火墙拦截。但更隐蔽的问题是:C++ 进程启动时 registry 和 exposer 生命周期管理出错——比如 registry 是局部变量,或 exposer 被析构了,metrics endpoint 就立即失效,但进程仍在跑,日志也不报错。
- 确认
exposer对象生命周期长于主逻辑(建议 static 或全局智能指针持有) - curl 测试:
curl http://localhost:9091/metrics,返回 200 且含你的指标名才算真正就绪 - 检查 Prometheus targets 页面是否显示 “DOWN”,点进去看 error message —— 常见是 connection refused(server 没起来)或 timeout(网络不通)
- 多线程环境下,
Counter::Increment()和Histogram::Observe()是线程安全的,但别在析构期调用它们
延迟指标桶数量膨胀很快,如果每秒打点几百次、又设了 20+ 个分桶,/metrics 响应体可能超 1MB,触发某些 proxy 的截断。这时得精简分桶或降采样,不是加内存能解决的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











