PHP "真异步" TrueAsync SAPI 与 NGINX Unit 集成

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::onRequest() - 注册请求处理器

用户代码(entrypoint.php)

  • 通过 HttpServer::onRequest() 注册处理器
  • 使用 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) 时:

  1. 数据通过 nxt_unit_response_write_nb() 发送
  2. 缓冲区满了,剩余数据进 drain_queue
  3. 缓冲区空出来,触发 shm_ack_handler
  4. 异步写入,不阻塞协程

配置

unit-config.json

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。

加载配置

bash 复制代码
curl -X PUT --data-binary @unit-config.json \
  --unix-socket /tmp/unit/control.unit.sock \
  http://localhost/config

entrypoint.php

基本结构:

php 复制代码
<?php

use NginxUnit\HttpServer;
use NginxUnit\Request;
use NginxUnit\Response;

set_time_limit(0);

// 注册请求处理器
HttpServer::onRequest(static function (Request $request, Response $response) {
    // 拿请求数据
    $method = $request->getMethod();
    $uri = $request->getUri();

    // 设响应头
    $response->setHeader('Content-Type', 'application/json');
    $response->setStatus(200);

    // 发数据(非阻塞)
    $response->write(json_encode([
        'message' => 'Hello from TrueAsync!',
        'method' => $method,
        'uri' => $uri
    ]));

    // 结束响应
    $response->end();
});

API 参考

Request

  • getMethod(): string - HTTP 方法(GET、POST 等)
  • getUri(): string - 请求 URI
  • getRequestContext(): ?mixed - 请求上下文(TODO)
  • getRequestContextParameters(): ?mixed - 上下文参数(TODO)
  • createResponse(): Response - 创建 Response 对象(通常不需要)

Response

  • setStatus(int $code): bool - 设置 HTTP 状态码
  • setHeader(string $name, string $value): bool - 添加响应头
  • write(string $data): bool - 发送数据(非阻塞操作)
  • end(): bool - 完成响应并释放资源

注意

  • setStatus()setHeader() 要在第一次 write() 之前调用
  • 调用过 write() 后,响应头就发出去了
  • end() 必须调用,完成请求

生命周期

php 复制代码
HttpServer::onRequest(function (Request $req, Response $resp) {
    // 1. 响应头还能改
    $resp->setStatus(200);
    $resp->setHeader('Content-Type', 'text/plain');

    // 2. 第一次 write() 把响应头发出去了
    $resp->write('Hello ');

    // 3. 现在响应头改不了了
    // $resp->setHeader() → 报错!

    // 4. 可以继续写数据
    $resp->write('World!');

    // 5. 结束请求(必须调!)
    $resp->end();
});

运行和测试

启动 NGINX Unit

bash 复制代码
./build/sbin/unitd \
  --no-daemon \
  --log /tmp/unit/unit.log \
  --state /tmp/unit \
  --control unix:/tmp/unit/control.unit.sock \
  --pid /tmp/unit/unit.pid \
  --modules ./build/lib/unit/modules

重要--modules 参数必须加,用来加载 PHP 模块。

查看日志

bash 复制代码
tail -f /tmp/unit/unit.log

测试

bash 复制代码
curl http://127.0.0.1:8080/

响应:

json 复制代码
{
    "message": "Hello from NginxUnit TrueAsync HttpServer!",
    "method": "GET",
    "uri": "/",
    "timestamp": "2025-10-04 15:30:00"
}

负载测试

bash 复制代码
wrk -t4 -c100 -d30s http://127.0.0.1:8080/

调试

GDB

bash 复制代码
gdb ./build/sbin/unitd
(gdb) set follow-fork-mode child
(gdb) run --no-daemon --log /tmp/unit/unit.log ...

设置断点

复制代码
break nxt_php_request_handler
break nxt_php_request_coroutine_entry
break nxt_unit_response_write_nb

实用命令

bash 复制代码
# 停止所有 NGINX Unit 进程
pkill -9 unitd

# 检查控制套接字
ls -la /tmp/unit/control.unit.sock

# 获取当前配置
curl --unix-socket /tmp/unit/control.unit.sock http://localhost/config

内部实现

初始化

  1. nxt_php_extension_init()NginxUnit 命名空间注册类
  2. worker 启动时加载 entrypoint.php
  3. HttpServer::onRequest() 把回调存到 nxt_php_request_callback

请求处理

  1. NGINX Unit 调用 nxt_php_request_handler(req)
  2. 创建协程:zend_async_coroutine_create(nxt_php_request_coroutine_entry)
  3. 协程指针存到 req
  4. 协程加入激活队列
  5. 控制权回到事件循环 nxt_unit_run()

协程激活

  1. 事件循环调用 nxt_unit_response_buf_alloc 回调
  2. 回调通过 zend_async_coroutine_activate() 激活协程
  3. 执行 nxt_php_request_coroutine_entry()
  4. 创建 PHP Request/Response 对象
  5. 调用用户回调
  6. response->end() 后协程结束

异步发送

  1. response->write()nxt_unit_response_write_nb()
  2. 没发完,剩下的进 drain_queue
  3. 缓冲区空了,触发 shm_ack_handler()
  4. shm_ack_handler 继续写,需要的话调 end()

未来计划

  • 实现 Request::getRequestContext()
  • 添加请求头支持
  • 添加 POST 数据解析
  • WebSocket 支持
  • 流式响应

总结

NGINX Unit TrueAsync PHP 集成让 PHP 真正拥有了异步处理能力。通过协程机制,单个进程可以同时处理多个请求,这在过去是无法想象的。

对于 PHP 生态来说,这是一个重要的转折点。传统的 PHP-FPM 模式下,每个请求独占一个进程,在高并发场景下资源消耗巨大。现在有了 TrueAsync,PHP 可以像 Node.js、Go 那样高效处理并发请求,同时保持语言本身的简洁性。

虽然目前还有一些功能在开发中,比如完整的请求头支持、POST 数据解析、WebSocket 等,但现有的功能已经足够构建高性能的 API 服务。非阻塞 I/O、协程调度、事件循环------这些核心机制都已经就位。

对于需要处理高并发请求的 PHP 应用,特别是 API 服务、微服务架构,NGINX Unit TrueAsync 提供了一个值得认真考虑的选择。它不需要改变太多现有代码结构,却能带来性能上的显著提升。

相关推荐
幽络源小助理1 分钟前
影视脚本分镜在线协作系统源码 PHP剧本创作平台
开发语言·php
渐儿4 分钟前
Dify 插件机制详解
后端
渐儿12 分钟前
Spring Boot 异步并发实现原理详解
后端
来一斤小鲜肉12 分钟前
Spring AI 多模态能力全景
后端·aigc
张立立13 分钟前
震惊!用Python每天早上8点,我准时给女神发早安,只因这个脚本…
后端·python
渐儿13 分钟前
Python 并行与并发:案例与实现
后端
神奇小汤圆16 分钟前
面试官问:让你设计一个消息队列,你会怎么答?
后端
techdashen24 分钟前
Cloudflare 如何用 Rust 构建一个高性能解释器
开发语言·后端·rust
sing~~28 分钟前
SpringCloud的了解和使用
后端·spring·spring cloud
神奇小汤圆36 分钟前
K8s生产环境那些文档不会告诉你的坑
后端