架构概览

OxPHP 是一个单二进制 HTTP 服务器,用于替代传统的 nginx + PHP-FPM 技术栈。它在单个进程中完成 HTTP 解析、TLS 终止、路由、PHP 执行、压缩和可观测性,运行时无需任何外部依赖。

OxPHP 的工作原理

OxPHP 在单个进程中融合了两个运行时层:

  1. 异步 HTTP 层。 一个事件驱动的网络层负责接受 TCP 连接、执行 TLS 握手、解析 HTTP 请求并发送响应。它使用非阻塞 I/O 处理数千个并发连接,因此一个慢速客户端永远不会阻塞另一个客户端。
  2. PHP 工作进程池。 一组专用的 PHP 工作进程执行你的 PHP 脚本。在标准模式下,每个工作进程一次处理一个请求。在启用了纤程多路复用工作进程模式下,单个工作进程可以同时服务多个并发请求:当脚本调用 oxphp_sleep()oxphp_async_await() 时,纤程让出线程,工作进程切换到下一个请求。
  3. 异步池(可选)。用于执行通过 oxphp_async() 提交的任务的独立操作系统线程。通过设置 ASYNC_WORKERS > 0 启用。它与工作进程池相互隔离,因此后台任务不会阻塞 HTTP 请求处理。

这两层通过一个有界队列进行通信。当一个需要执行 PHP 的 HTTP 请求到达时,异步层将其放入队列。空闲的 PHP 工作进程取出该请求,执行脚本,并将响应返回给异步层以交付给客户端。

这种分离意味着网络 I/O(接受连接、读取请求头、压缩响应、服务静态文件)永远不会与 PHP 执行争抢资源。每一层都能独立扩展。

工作进程池

PHP 工作进程池决定了可以并发执行多少个 PHP 脚本。OxPHP 支持两种池模式。

静态池

固定数量的工作进程在启动时创建,并在服务器的整个生命周期内保持运行。这是默认模式。

bash
PHP_WORKERS=8 # exactly 8 workers PHP_WORKERS=0 # auto-detect (default): half of available CPU cores, minimum 1

动态池

工作进程根据需求上下伸缩。用冒号分隔指定最小和最大数量:

bash
PHP_WORKERS=2:16 # start with 2, scale up to 16 under load

当所有当前工作进程都在忙碌时,OxPHP 会创建新的工作进程,直至达到最大数量。当某个工作进程空闲时间超过 PHP_WORKERS_IDLE_SECONDS(默认:30 秒)时,它会被回收,使数量向最小值靠拢。

队列与背压

在异步 HTTP 层和工作进程池之间是一个有界队列。它的容量默认为初始工作进程数乘以 128,可通过 QUEUE_CAPACITY 覆盖。对于静态池,初始数量就是配置的工作进程数。对于动态池(MIN:MAX),初始数量是最小值。

当队列已满(所有工作进程都在忙碌且队列已达到容量上限)时,OxPHP 会立即向客户端返回一个带有 Retry-After 头的 529 Site is Overloaded 响应。状态码 529(非标准,Cloudflare 等厂商在使用)能清晰地将过载与应用错误(500)和维护(503)区分开来,从而更容易配置告警和负载均衡器。

请求流程

无论是服务静态文件还是执行 PHP,每个请求都会经过相同的流水线:

graph TD
  Client(["Client"]) --> TLS["TLS termination<br/>(if configured)"]
  TLS --> Parse["HTTP parsing + Request ID"]
  Parse --> Proxy["Trusted proxy resolution<br/>(if TRUSTED_PROXIES set)"]
  Proxy --> Rate["Rate limiting check"]
  Rate --> Route{"Route resolution"}
  Route -->|Static file| Cache["File cache / disk read"]
  Cache --> Compress["Compression + Response headers"]
  Route -->|PHP request| Queue["Bounded queue<br/>(529 if full)"]
  Queue --> Worker["PHP worker executes script"]
  Worker --> Normal["Normal response"]
  Worker --> SSE["SSE streaming (chunked)"]
  Worker --> Early["Early response (finish_request)<br/>+ background work"]
  Compress --> Deliver(["Response to client"])
  Normal --> Deliver
  SSE --> Deliver
  Early --> Deliver
  1. TLS 终止。 如果配置了 TLS_CERTTLS_KEY,OxPHP 会直接处理 TLS,无需单独的反向代理。
  2. HTTP 解析与请求 ID。 请求被解析,并生成一个唯一的请求 ID(或保留传入的 X-Request-ID 头)。
  3. 可信代理解析。 如果设置了 TRUSTED_PROXIES 且连接方 IP 受信任,OxPHP 会从 Forwarded(RFC 7239)或 X-Forwarded-* 头中提取真实的客户端 IP、协议和主机。解析出的 IP 会用于后续所有步骤,包括限流和访问日志。参见可信代理
  4. 限流。 如果设置了 RATE_LIMIT,客户端的 IP 会与每个 IP 的请求计数器进行核对。超出限制的请求会立即收到 429 Too Many Requests 响应。
  5. 路由解析。 URL 会与配置的路由模式(传统、框架或 SPA)进行匹配。结果是一个静态文件、一个 PHP 脚本或一个 404。工作进程模式(如果启用)会改变 PHP 执行已解析脚本的方式,但不会改变路由解析本身。
  6. 静态文件。 直接从内存缓存(针对频繁访问的文件)提供,或从磁盘流式传输。OxPHP 会自动添加 ETagLast-ModifiedCache-Control 头。
  7. PHP 执行。 请求被放入有界队列,由空闲的工作进程取出。如果队列已满,客户端会立即收到 529。
  8. 压缩。 当客户端发送 Accept-Encoding: br 时,基于文本的响应会在发送前用 Brotli 压缩(可通过 COMPRESSION_LEVEL 配置)。
  9. SSE 流式传输。 如果脚本设置了 Content-Type: text/event-stream 或调用了 oxphp_stream_flush(),OxPHP 会切换到流式模式:每次 flush() 调用都会立即向客户端发送一个数据块,而不会缓冲整个响应。在工作进程模式下,SSE 与纤程多路复用协同工作。
  10. 提前响应。 调用 oxphp_finish_request() 会立即将 HTTP 响应发送给客户端。脚本会在后台继续执行(写日志、更新缓存、发送通知),而无需保持连接打开。
  11. 响应交付。 完成的响应通过连接发回,如果启用了访问日志,还会写入一条日志记录。

工作进程模式 vs 标准模式

OxPHP 支持两种 PHP 执行模型:

标准模式(默认)

为每个请求创建一个全新的 PHP 环境。自动加载器、配置和数据库连接在每个请求上初始化,并在之后销毁。这种模型开箱即用地兼容所有 PHP 应用。

工作进程模式

让 PHP 进程在多个请求之间保持存活。你的应用只引导一次(加载自动加载器、配置并建立数据库连接),然后进入请求循环。在两个请求之间,OxPHP 会自动重置超全局变量、输出缓冲区和响应头,同时保留已引导的状态。

工作进程模式消除了每个请求的启动开销,对于引导成本高昂的框架类应用(Laravel、Symfony 等),这可以显著缩短响应时间。

要启用工作进程模式,请设置 WORKER_MODE_ENABLED=true,并让 ENTRY_FILE 指向一个调用 oxphp_worker() 的 PHP 脚本:

php
<?php require __DIR__ . '/../vendor/autoload.php'; $app = new MyApp\Application(); oxphp_worker(function () use ($app) { $app->handle(); });

有关详细指南,请参见工作进程模式

内部服务器

如果设置了 INTERNAL_ADDR 变量,OxPHP 会在指定端口上启动一个独立的 HTTP 服务器。它提供三个端点:

端点 描述
GET /health JSON 格式的健康状态(运行时长、请求计数器、连接数、工作进程状态)。正常运行时返回 200,性能降级时返回 503
GET /metrics Prometheus 格式的指标——请求计数器、响应时间、队列等待时间、工作进程统计、压缩节省量。
GET /config JSON 格式的当前活动配置快照。TLS 文件路径会被脱敏。

内部服务器不经过 PHP 工作进程池或有界队列。它直接从异步 HTTP 层响应,因此即使 PHP 池满载,它仍然可以访问。这使得 /health 适合用于 Kubernetes 的存活/就绪探针。

有关详情,请参见内部服务器

安全性

OxPHP 提供了多项保障,让你的应用在生产环境中可靠运行:

  • 请求隔离。 如果一个 PHP 脚本崩溃或触发致命错误,只有那一个请求会受到影响。服务器会继续正常处理所有其他请求。崩溃的工作进程会被自动替换为一个全新的工作进程。
  • 工作进程自动重生。 OxPHP 会监控所有 PHP 工作进程的健康状况。如果某个工作进程意外死亡,会自动启动一个新的工作进程来接替它,无需手动干预。
  • 背压保护。 有界请求队列可防止过载。当服务器达到容量上限时,新请求会收到一个带有 Retry-After 头的 529 响应,而不是无限期排队并引发级联超时。
  • 路径遍历保护。 所有 URL 路径在访问文件系统之前都会经过清理。百分号编码的遍历尝试、.. 片段以及逃逸出文档根目录的路径都会被阻止。
  • 优雅关闭。 收到 SIGTERM 或 SIGINT(Ctrl+C)时,OxPHP 会停止接受新连接,并等待处理中的请求完成(最长等待一个可配置的排空超时时间),然后再退出。

参见