架构概览
OxPHP 是一个单二进制 HTTP 服务器,用于替代传统的 nginx + PHP-FPM 技术栈。它在单个进程中完成 HTTP 解析、TLS 终止、路由、PHP 执行、压缩和可观测性,运行时无需任何外部依赖。
OxPHP 的工作原理
OxPHP 在单个进程中融合了两个运行时层:
- 异步 HTTP 层。 一个事件驱动的网络层负责接受 TCP 连接、执行 TLS 握手、解析 HTTP 请求并发送响应。它使用非阻塞 I/O 处理数千个并发连接,因此一个慢速客户端永远不会阻塞另一个客户端。
- PHP 工作进程池。 一组专用的 PHP 工作进程执行你的 PHP 脚本。在标准模式下,每个工作进程一次处理一个请求。在启用了纤程多路复用的工作进程模式下,单个工作进程可以同时服务多个并发请求:当脚本调用
oxphp_sleep()或oxphp_async_await()时,纤程让出线程,工作进程切换到下一个请求。 - 异步池(可选)。用于执行通过
oxphp_async()提交的任务的独立操作系统线程。通过设置ASYNC_WORKERS > 0启用。它与工作进程池相互隔离,因此后台任务不会阻塞 HTTP 请求处理。
这两层通过一个有界队列进行通信。当一个需要执行 PHP 的 HTTP 请求到达时,异步层将其放入队列。空闲的 PHP 工作进程取出该请求,执行脚本,并将响应返回给异步层以交付给客户端。
这种分离意味着网络 I/O(接受连接、读取请求头、压缩响应、服务静态文件)永远不会与 PHP 执行争抢资源。每一层都能独立扩展。
工作进程池
PHP 工作进程池决定了可以并发执行多少个 PHP 脚本。OxPHP 支持两种池模式。
静态池
固定数量的工作进程在启动时创建,并在服务器的整个生命周期内保持运行。这是默认模式。
PHP_WORKERS=8 # exactly 8 workers
PHP_WORKERS=0 # auto-detect (default): half of available CPU cores, minimum 1动态池
工作进程根据需求上下伸缩。用冒号分隔指定最小和最大数量:
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
- TLS 终止。 如果配置了
TLS_CERT和TLS_KEY,OxPHP 会直接处理 TLS,无需单独的反向代理。 - HTTP 解析与请求 ID。 请求被解析,并生成一个唯一的请求 ID(或保留传入的
X-Request-ID头)。 - 可信代理解析。 如果设置了
TRUSTED_PROXIES且连接方 IP 受信任,OxPHP 会从Forwarded(RFC 7239)或X-Forwarded-*头中提取真实的客户端 IP、协议和主机。解析出的 IP 会用于后续所有步骤,包括限流和访问日志。参见可信代理。 - 限流。 如果设置了
RATE_LIMIT,客户端的 IP 会与每个 IP 的请求计数器进行核对。超出限制的请求会立即收到429 Too Many Requests响应。 - 路由解析。 URL 会与配置的路由模式(传统、框架或 SPA)进行匹配。结果是一个静态文件、一个 PHP 脚本或一个 404。工作进程模式(如果启用)会改变 PHP 执行已解析脚本的方式,但不会改变路由解析本身。
- 静态文件。 直接从内存缓存(针对频繁访问的文件)提供,或从磁盘流式传输。OxPHP 会自动添加
ETag、Last-Modified和Cache-Control头。 - PHP 执行。 请求被放入有界队列,由空闲的工作进程取出。如果队列已满,客户端会立即收到 529。
- 压缩。 当客户端发送
Accept-Encoding: br时,基于文本的响应会在发送前用 Brotli 压缩(可通过COMPRESSION_LEVEL配置)。 - SSE 流式传输。 如果脚本设置了
Content-Type: text/event-stream或调用了oxphp_stream_flush(),OxPHP 会切换到流式模式:每次flush()调用都会立即向客户端发送一个数据块,而不会缓冲整个响应。在工作进程模式下,SSE 与纤程多路复用协同工作。 - 提前响应。 调用
oxphp_finish_request()会立即将 HTTP 响应发送给客户端。脚本会在后台继续执行(写日志、更新缓存、发送通知),而无需保持连接打开。 - 响应交付。 完成的响应通过连接发回,如果启用了访问日志,还会写入一条日志记录。
工作进程模式 vs 标准模式
OxPHP 支持两种 PHP 执行模型:
为每个请求创建一个全新的 PHP 环境。自动加载器、配置和数据库连接在每个请求上初始化,并在之后销毁。这种模型开箱即用地兼容所有 PHP 应用。
让 PHP 进程在多个请求之间保持存活。你的应用只引导一次(加载自动加载器、配置并建立数据库连接),然后进入请求循环。在两个请求之间,OxPHP 会自动重置超全局变量、输出缓冲区和响应头,同时保留已引导的状态。
工作进程模式消除了每个请求的启动开销,对于引导成本高昂的框架类应用(Laravel、Symfony 等),这可以显著缩短响应时间。
要启用工作进程模式,请设置 WORKER_MODE_ENABLED=true,并让 ENTRY_FILE 指向一个调用 oxphp_worker() 的 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 会停止接受新连接,并等待处理中的请求完成(最长等待一个可配置的排空超时时间),然后再退出。