Worker 类
OxPHP\Server\Worker 是与单个 OxPHP 操作系统工作线程相关的一切内容的统一运行时句柄。它是对桥接层线程本地状态的 final、无状态包装器,由 SAPI 扩展自身注册,因此在传统模式和工作进程模式下始终可用。每次调用都直接从运行时读取实时状态;对象本身不缓存任何内容。
Worker::current() 为每个操作系统线程返回一个单例:在同一线程上调用两次总是返回同一个实例。
快速参考
| 方法 | 说明 |
|---|---|
Worker::current(): self |
返回当前操作系统线程的单例句柄。 |
Worker::isWorkerMode(): bool |
如果服务器运行在工作进程模式下(即 WORKER_MODE_ENABLED=true),返回 true。 |
id(): int |
当前操作系统线程的数字工作进程标识符,取值范围为 0..N-1。 |
startTime(): float |
该操作系统工作线程被创建时的 Unix 时间戳(秒)。 |
requestCount(): int |
该操作系统线程处理的请求数量(从 1 开始计数)。在两种模式下都会递增。 |
memoryUsage(): int |
实时 PHP 内存使用量(字节,zend_memory_usage(0))。 |
rss(): int |
进程常驻内存集大小(字节)。不缓存——每个请求最多调用一次。 |
maxMemoryBytes(): int |
配置的内存上限(字节)。0 表示无限制。 |
scheduleExit(): void |
标记工作进程在当前请求完成后优雅退出。在传统模式下为空操作。 |
isExitScheduled(): bool |
如果已对当前工作进程调用过 scheduleExit(),返回 true。在传统模式下始终为 false。 |
exitReason(): ?string |
待处理的退出原因:'scheduled'、'max_memory'、'error',当没有待处理的退出时为 null。在传统模式下始终为 null。 |
serve(callable $h): void |
进入请求循环。在工作进程模式之外抛出 InvalidServeContextException。 |
模式对照表
| 方法 | 传统模式 | 工作进程模式 |
|---|---|---|
current() |
每个操作系统线程一个单例。 | 每个操作系统线程一个单例。 |
isWorkerMode() |
false |
true |
id() |
工作进程池中的操作系统线程索引。 | 工作进程池中的操作系统线程索引。 |
startTime() |
操作系统线程被创建的时间(通常为服务器启动时)。 | 操作系统线程被创建的时间。 |
requestCount() |
从 1 开始,在复用同一操作系统线程的多个请求间递增(1, 2, 3, …)。 |
从 1 开始,每处理一个由该工作进程处理的请求就递增。 |
memoryUsage() |
调用时刻的实时 PHP 内存。 | 调用时刻的实时 PHP 内存。 |
rss() |
实时进程 RSS。 | 实时进程 RSS。 |
maxMemoryBytes() |
0(不应用回收上限)。 |
WORKER_MAX_MEMORY_MIB 的值 × 1 MiB,未设置时为 0。 |
scheduleExit() |
空操作(脚本无论如何都会退出)。 | 设置退出标志;请求循环在当前处理器返回后停止。 |
isExitScheduled() |
始终为 false。 |
在此线程上调用过 scheduleExit() 后为 true。 |
exitReason() |
始终为 null。 |
在有待处理的退出之前为 null;之后为 'scheduled'、'max_memory'、'error' 之一。 |
serve(callable) |
抛出 OxPHP\Server\Exception\InvalidServeContextException。 |
进入请求循环。 |
示例
按工作进程划分的日志上下文
为每一行日志打上工作进程 id 和每线程请求计数器的标记,这样你就能将请求流量与特定工作进程关联起来。
<?php
$worker = OxPHP\Server\Worker::current();
$logger->info('handling request', [
'worker_id' => $worker->id(),
'request_number' => $worker->requestCount(),
]);每个操作系统线程只引导一次
requestCount() 从 1 开始计数,因此任何线程处理的第一个请求看到的值都是 1。这里是运行按线程惰性初始化的可移植位置——这类初始化应当恰好只发生一次。
<?php
$worker = OxPHP\Server\Worker::current();
if ($worker->requestCount() === 1) {
bootstrap();
}scheduleExit
由应用驱动的工作进程回收。当前请求正常完成;随后循环检查 isExitScheduled() 并跳出。监督进程重新创建一个新的工作进程,再次运行工作进程文件的外层作用域。
<?php
$worker = OxPHP\Server\Worker::current();
handleRequest();
// Reload bootstrap on every request when developing locally.
if (getenv('OXPHP_DEV') === '1') {
$worker->scheduleExit();
}scheduleExit() 是幂等的,在工作进程模式之外是空操作。使用场景:
-
开发期间的热重载。 每个请求后退出,使外层作用域的引导代码重新运行。
-
基于 RSS 的回收。
WORKER_MAX_MEMORY_MIB只测量 Zend 分配器。对于大量使用扩展的技术栈(curl、mysqli),当进程 RSS 超过你自己设定的阈值时,你还可以额外进行回收:if ($worker->rss() > 256 * 1024 * 1024) { $worker->scheduleExit(); } -
协调式滚动重启。 用一个哨兵文件或信号来控制该调用,使外部编排器能够干净地排空工作进程。
工作进程入口点
在工作进程引导脚本中,调用 serve() 进入请求循环。
<?php
require __DIR__ . '/../vendor/autoload.php';
OxPHP\Server\Worker::current()->serve(function () {
handleRequest();
});RSS 可观测性
rss() 返回实时进程常驻内存集大小(字节)。这个调用是一次真正的系统调用:开销很低,但并非零成本。每个请求最多读取一次。
<?php
$worker = OxPHP\Server\Worker::current();
$rss = $worker->rss();
$metrics->gauge('php_worker_rss_bytes', $rss, [
'worker_id' => (string) $worker->id(),
]);从 oxphp_* 函数迁移
这些旧的自由函数仍然可用,并路由到相同的内部状态。它们没有被弃用。新代码应优先使用类 API,以获得更好的可发现性和一致性。
| 旧函数 | 类 API |
|---|---|
oxphp_is_worker() |
OxPHP\Server\Worker::isWorkerMode() |
oxphp_worker_id() |
OxPHP\Server\Worker::current()->id() |
oxphp_worker(callable) |
OxPHP\Server\Worker::current()->serve(callable) |
注意事项
rss()不做缓存。 每次调用都会执行一次系统调用(在 Linux 上读取/proc/self/statm,在 macOS 上使用getrusage(RUSAGE_SELF))。开销很低但并非零成本,因此每个请求最多调用一次,通常放在指标处理器内部,而不是每一行日志里都调用。- 禁止克隆。
clone $worker会抛出\Error("Cloning OxPHP\\Server\\Worker is not allowed")。工作进程句柄代表操作系统线程的身份;克隆它会造成同一线程存在第二个句柄的误导性印象。 - 在 OxPHP 宿主之外(例如当一个链接了 SAPI 的扩展被加载到 PHP CLI 中时),
Worker::current()仍会返回一个实例,但每个访问器都会返回其零状态值:id()为0,startTime()为进程启动时间,requestCount()为0,rss()为实时 RSS,而serve()会抛出InvalidServeContextException。
另请参阅
- 工作进程模式 —— 持久化 PHP 进程和“只引导一次”模式的概览
- PHP 函数 —— 旧的
oxphp_*自由函数的参考 - Request API —— 用于每请求计时的
OxPHP\Http\RequestInterface::startTime()