workerman需手动实现prometheus指标接口,用独立httpworker监听/metrics,通过apcu/redis共享计数器,避免多进程状态不一致;须正确统计连接数、暴露合理指标类型,并在prometheus配置中启用honor_labels。

Workerman 本身不暴露 Prometheus metrics,必须自己加
Workerman 是纯 PHP 的常驻进程框架,没有内置指标导出能力。想被 Prometheus 抓取,得手动实现一个 HTTP 接口返回符合 text/plain; version=0.0.4 格式的指标文本。不能直接用 prometheus_client_php 这类库——它默认依赖 PSR-7 和事件循环,而 Workerman 的 HTTP server 不兼容标准中间件链。
实操建议:
- 在 Worker 启动后,用
Worker::onWorkerStart注册一个独立的HttpWorker,监听如:9502/metrics - 指标数据必须是全局共享的:用
apcu_store或redis存储计数器(比如连接数、请求量),避免多进程间状态不一致 - 每次请求
/metrics时,读取当前值并格式化为 Prometheus 文本:以# HELP开头,# TYPE声明类型,最后是metric_name{label="value"} value timestamp - 别在响应里写
Content-Type: application/openmetrics-text—— Prometheus 2.30+ 才支持 OpenMetrics,老版本只认text/plain
抓取配置要避开 Workerman 多进程导致的 scrape 冲突
Prometheus 默认每 15s 抓一次,但 Workerman 启动多个子进程后,如果所有进程都监听同一个端口(比如 9502),就会出现端口占用或响应错乱。更麻烦的是:Prometheus 抓到的可能是任意一个子进程的瞬时快照,无法反映整体负载。
解决思路:
- 让每个 Workerman 子进程绑定不同端口(如 9502、9503…),再在 Prometheus 的
scrape_configs中用static_configs列出全部地址 —— 但 Grafana 查看时需用sum by(job)(workerman_connections)聚合,否则会看到多条线 - 更推荐方式:只开一个 metrics 端口,由主进程或单独的 Collector 进程统一聚合各子进程数据(通过
pcntl_signal或共享内存通知),再对外暴露单一 endpoint - 务必在
scrape_configs中加honor_labels: true,否则 Workerman 进程名、worker_id 这类 label 会被 Prometheus 强制覆盖
Grafana 里画 Workerman 连接数曲线容易误判活跃连接
workerman_connections 这个指标如果只是简单采集 swoole_server->connection_list() 或 Worker::$connections 的长度,会把已断开但尚未被回收的 socket 也计入。尤其在高并发短连接场景下,曲线会持续上扬,看起来像连接泄漏。
正确做法:
- 在
Worker::onClose回调里主动递减连接计数器(用apcu_dec或 redisDECR) - 避免用
count(Worker::$connections)—— 它包含未完成握手的半连接,应改用Worker::$statistics['connection_count'](需开启Worker::$daemonize = false并确保统计开关打开) - 在 Grafana 面板中,对
workerman_connections做rate(workerman_connections[5m])没意义(它是 Gauge 类型),应该直接画原始值,再叠加workerman_close_total(Counter)观察关闭速率是否匹配
告警规则里别直接用 raw metrics 做阈值判断
比如写一条 workerman_connections > 5000 就发告警,大概率误报。因为 Workerman 的连接数天然有脉冲特性:秒级内可能从几百飙到三千,又快速回落,这种波动是正常的。
更稳妥的方式:
- 用
avg_over_time(workerman_connections[2m])消除毛刺,再和阈值比 - 结合
workerman_worker_status{status="idle"}(需自行暴露空闲 worker 数)判断是否真有瓶颈:如果连接数高但 idle worker 多,说明不是处理不过来,而是业务逻辑阻塞了 I/O - 给
workerman_request_duration_seconds(需埋点记录每个请求耗时)配histogram_quantile(0.95, sum(rate(workerman_request_duration_seconds_bucket[5m])) by (le)),比单纯看平均值更能发现长尾问题
Workerman 的监控难点不在数据采集,而在理解它的生命周期模型——连接、请求、worker 状态三者不是线性对应关系。指标设计时稍一偷懒,Grafana 上看到的就全是“假信号”。











