PHP 8.6+ 应直接选用内置 __perf__ 面板 + OpenTelemetry 导出,因其支持按 task ID 追踪完整生命周期、自动捕获协程切换与阻塞 I/O,并可对接 Jaeger/Tempo 实现跨服务追踪,而 Zabbix 和 Prometheus 无法感知任务上下文与函数级堆栈。

PHP分布式任务框架该用哪个监控面板
直接选 PHP 8.6+ 内置 __perf__ 面板 + OpenTelemetry 导出,别碰 Zabbix 或 Prometheus 单独拉取任务队列状态——它们看不到任务执行上下文、不感知协程生命周期、也拿不到函数级耗时堆栈。
原因很简单:分布式任务(如 Swoole Task Worker、RoadRunner 的 Worker、或基于 Redis Queue 的 Laravel Horizon)本质是长时运行、多并发、带状态迁移的进程模型。Zabbix 的被动采集依赖 PHP-FPM /status,而任务框架根本不走 FPM;Prometheus 的 /metrics 暴露适合请求-响应模型,但对“一个任务跑 3 分钟、中途重试 2 次、卡在 DB 连接池”的场景,指标维度严重不足。
PHP 8.6 起内置的监控能力已支持:
- 按 task ID 追踪单次执行的完整生命周期(start → dispatch → execute → finish/fail)
- 自动捕获协程切换点与阻塞 I/O(如
co::sleep、go()中的 MySQL 查询) - 与 OpenTelemetry SDK 对齐,可将 task span 发往 Jaeger/Tempo 做跨服务追踪
如何让 Swoole/RoadRunner 任务暴露 __perf__ 面板
不是所有 SAPI 都默认启用该面板。关键在启动方式和配置项是否生效:
立即学习“PHP免费学习笔记(深入)”;
- 确认 PHP 编译时启用了
--enable-opcache且opcache.enable_cli=1已写入php.ini - Swoole 启动需用 CLI 模式,并显式传入
-d php_monitor.enabled=1:php -d php_monitor.enabled=1 -d php_monitor.endpoint=/__perf__ worker.php - RoadRunner 要在
.rr.yaml中配env注入:env: { PHP_MONITOR_ENABLED: "1", PHP_MONITOR_ENDPOINT: "/__perf__" } - 面板仅在 HTTP 请求命中
/__perf__且携带正确 token 时返回 JSON 数据,不拦截正常任务逻辑
注意:若用 php-fpm 管理任务 worker(极不推荐),__perf__ 不可用——FPM SAPI 禁用了该功能。
opcache_get_status() 和 memory_get_usage() 在任务中怎么采才准
任务常驻内存、复用进程,直接调用这两个函数会污染数据:上一个任务的 OPcache 统计、内存残留会带到下一个任务里。
必须在任务入口处做「快照隔离」:
- 用
opcache_get_status(['scripts' => false])关闭脚本列表,只取聚合指标(opcache_hit_rate,memory_usage),避免遍历缓存脚本带来的性能抖动 - 内存采集改用
memory_get_peak_usage(true),true表示从分配器底层读,绕过 PHP 用户态缓存 - 每次任务开始前调用
gc_collect_cycles(),强制回收上一轮可能残留的循环引用,让memory_get_usage()更真实 - 不要在任务中间高频调用——任务执行中内存波动剧烈,采样间隔建议 ≥ 500ms,否则指标噪声大过信号
OpenTelemetry 埋点怎么避免拖慢任务吞吐
全链路埋点开销实测在 15%~25%,对高吞吐任务系统不可接受。必须做三件事:
- 只对
critical任务类型开启 span(比如支付回调、库存扣减),用环境变量控制:if (getenv('OTEL_TASK_CRITICAL') === '1') { $span = $tracer->startSpan(...); } - 禁用 span attribute 的自动注入(如
http.request.headers),手动只设必要字段:$span->setAttribute('task.id', $id)、$span->setAttribute('task.retry_count', $retry) - 导出器必须用异步 batch 方式:
OtlpHttpExporter配max_queue_size=1000+schedule_delay_millis=1000,防止网络卡顿阻塞任务主流程
真正容易被忽略的是:任务失败重试时,OpenTelemetry 默认为每次重试新建 span,导致同一逻辑出现多个同名 span。必须在重试前手动结束旧 span 并复用 trace_id,否则追踪链断裂。



















