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
<?php $worker = OxPHP\Server\Worker::current(); $logger->info('handling request', [ 'worker_id' => $worker->id(), 'request_number' => $worker->requestCount(), ]);

每个操作系统线程只引导一次

requestCount() 从 1 开始计数,因此任何线程处理的第一个请求看到的值都是 1。这里是运行按线程惰性初始化的可移植位置——这类初始化应当恰好只发生一次。

php
<?php $worker = OxPHP\Server\Worker::current(); if ($worker->requestCount() === 1) { bootstrap(); }

scheduleExit

由应用驱动的工作进程回收。当前请求正常完成;随后循环检查 isExitScheduled() 并跳出。监督进程重新创建一个新的工作进程,再次运行工作进程文件的外层作用域。

php
<?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 超过你自己设定的阈值时,你还可以额外进行回收:

    php
    if ($worker->rss() > 256 * 1024 * 1024) { $worker->scheduleExit(); }
  • 协调式滚动重启。 用一个哨兵文件或信号来控制该调用,使外部编排器能够干净地排空工作进程。

工作进程入口点

在工作进程引导脚本中,调用 serve() 进入请求循环。

php
<?php require __DIR__ . '/../vendor/autoload.php'; OxPHP\Server\Worker::current()->serve(function () { handleRequest(); });

RSS 可观测性

rss() 返回实时进程常驻内存集大小(字节)。这个调用是一次真正的系统调用:开销很低,但并非零成本。每个请求最多读取一次。

php
<?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()0startTime() 为进程启动时间,requestCount()0rss() 为实时 RSS,而 serve() 会抛出 InvalidServeContextException

另请参阅

  • 工作进程模式 —— 持久化 PHP 进程和“只引导一次”模式的概览
  • PHP 函数 —— 旧的 oxphp_* 自由函数的参考
  • Request API —— 用于每请求计时的 OxPHP\Http\RequestInterface::startTime()