PHP "真异步" TrueAsync SAPI 与 NGINX Unit 集成
现在的 Web 开发和过去最大的区别是什么?一句话:没人再愿意等服务器响应了。
七八年前,甚至更早的时候,模块加载、组件打包、脚本解释、数据库查询——这些步骤慢一点,对业务和用户也不会造成太大影响。
现在不一样了。Web 开发的核心已经变成了最大化服务器响应速度。这种转变来自网速的提升和单页应用(SPA)的普及。对后端来说,就是要能处理海量的快速请求,还得把负载分配好。
经典的双池架构(请求 worker + 任务 worker)不是凭空出现的。
一个请求一个进程的模型,根本扛不住大批量的轻量请求。该上并发了——一个进程同时处理多个请求。
并发处理带来了新要求:服务器代码要尽可能贴近业务逻辑。以前不是这样的。以前可以用 CGI 或 FPM 把 Web 服务器和脚本文件分得清清楚楚,很优雅。现在这招不好使了。
所以现在的方案要么就是把组件集成得尽量紧密,要么干脆把 Web 服务器当内部模块嵌进去。NGINX Unit 就是这么干的——它把 JavaScript、Python、Go 这些语言直接嵌到 worker 模块里。PHP 也有模块,但一直以来,PHP 在这种直接集成里没捞到什么好处,因为还是一个 worker 只能处理一个请求。
原文 PHP "真异步" TrueAsync SAPI 与 NGINX Unit 集成
集成特性
架构
这个集成分三层:
C 层(nxt_php_sapi.c, nxt_php_extension.c)
- 在 PHP 里注册 TrueAsync SAPI
- 给每个请求创建协程
- 通过 nxt_unit_run() 管理事件循环
- 通过 nxt_unit_response_write_nb() 实现非阻塞数据传输
PHP 扩展层(NginxUnit 命名空间)
- NginxUnit\Request - 请求对象
- NginxUnit\Response - 响应对象,支持非阻塞发送
- NginxUnit\HttpServer:
nRequest() - 注册请求处理器
用户代码(entrypoint.php)
- 通过 HttpServer:
nRequest() 注册处理器
- 使用 Request/Response API
- 完全异步执行
请求流程
- HTTP 请求 → NGINX Unit → nxt_php_request_handler()
- ↓
- 创建协程 (zend_async_coroutine_create)
- ↓
- nxt_php_request_coroutine_entry()
- ↓
- 创建 Request/Response 对象
- ↓
- 调用 entrypoint.php 中的回调函数
- ↓
- response->write() → nxt_unit_response_write_nb()
- ↓
- response->end() → nxt_unit_request_done()
复制代码 非阻塞 I/O
调用 $response->write($data) 时:
- 数据通过 nxt_unit_response_write_nb() 发送
- 缓冲区满了,剩余数据进 drain_queue
- 缓冲区空出来,触发 shm_ack_handler
- 异步写入,不阻塞协程
配置
unit-config.json
- {
- "applications": {
- "my-php-async-app": {
- "type": "php",
- "async": true, // 启用 TrueAsync 模式
- "processes": 2, // 工作器数量
- "entrypoint": "/path/to/entrypoint.php",
- "working_directory": "/path/to/",
- "root": "/path/to/"
- }
- },
- "listeners": {
- "127.0.0.1:8080": {
- "pass": "applications/my-php-async-app"
- }
- }
- }
复制代码 重要:"async": true 会激活 TrueAsync SAPI,而不是标准的 PHP SAPI。
加载配置
- curl -X PUT --data-binary @unit-config.json \
- --unix-socket /tmp/unit/control.unit.sock \
- http://localhost/config
复制代码 entrypoint.php
基本结构:
[code] |