FrankenAsync 深度技术研究报告

FrankenAsync 是由 Johan Janssens 开发的实验性并发框架,作为 ConFoo 2026 会议演讲《PHP 150x Faster, Still Legacy-Friendly》的配套参考实现 。它展示了如何利用 FrankenPHP 的线程模型实现真正的并行执行,而无需重写阻塞式 PHP 代…

目录
  1. 项目概述
  2. 核心架构解析
  3. 1. 双层并发控制模型
  4. 2. 关键技术突破:跨语言调用链
  5. API 设计与使用模式
  6. 核心原语:Script + Future
  7. 结构化并发模式
  8. 性能特征与基准测试
  9. 声称的性能数据
  10. 与 ReactPHP/Amp 的对比
  11. 技术限制与先决条件
  12. 1. 构建要求
  13. 2. 架构限制
  14. 3. 资源消耗
  15. 项目结构解析
  16. 总结与评估
  17. 优势
  18. 局限
  19. 适用场景

项目概述

FrankenAsync 是由 Johan Janssens 开发的实验性并发框架,作为 ConFoo 2026 会议演讲《PHP 150x Faster, Still Legacy-Friendly》的配套参考实现 。它展示了如何利用 FrankenPHP 的线程模型实现真正的并行执行,而无需重写阻塞式 PHP 代码。

重要提示:官方明确声明这是参考实现/概念验证,而非生产级框架。代码采用 MIT 许可,鼓励社区探索、fork 和适配 。

核心架构解析

1. 双层并发控制模型

FrankenAsync 通过两个关键机制控制并发:

层级技术实现配置项默认值作用
PHP 执行层固定线程池FRANKENASYNC_THREADS4 × CPU承载实际 PHP 脚本的 FrankenPHP 线程池
Go 调度层信号量滑动窗口FRANKENASYNC_WORKERSthreads - 2限制并发 Go goroutine 数量,超限时排队
工作原理:当任务数超过信号量限制时,多余任务会在 Go 层队列中等待,形成滑动窗口执行模式 。

执行流程:
PHP Script::async() → C 扩展 → Go Task Manager (信号量控制) → FrankenPHP 线程池
      ↑                                                      ↓
      └────────── Future::awaitAll() ← 结果收集 ← 子请求执行完毕

2. 关键技术突破:跨语言调用链

FrankenAsync 最大的技术挑战是从 PHP 用户态穿越到 Go 运行时。标准 FrankenPHP 未暴露线程内部机制,因此项目依赖一个 Fork 版本,添加了关键 API :

新增 API语言作用
frankenphp.Thread(index)Go通过索引获取 PHP 线程的 *http.Request 上下文
frankenphp_thread_index()C从 C 扩展获取当前线程索引,实现双向桥接
完整调用链示例:
// PHP 层
(new Script('task.php'))->async(['id' => 1])
↓ CGO 桥接
// C 扩展层 (phpext.c)
PHP_METHOD(Script, async) {
    int thread_idx = frankenphp_thread_index(); // 获取当前线程 ID
    go_execute_script_async(thread_idx, ...);   // 调用 Go 函数
}
↓ Go 运行时
// Go 层 (phpext.go)
func go_execute_script_async(index C.int, ...) {
    req := frankenphp.Thread(int(index))  // 获取请求上下文
    manager := asynctask.FromContext(req.Context())
    manager.Async(runnable)                 // 提交到信号量控制的任务队列
}


API 设计与使用模式

核心原语:Script + Future

FrankenAsync 仅暴露两个核心原语,其余功能通过标准 PHP 生成器组合实现 :

use Frankenphp\Script;
use Frankenphp\Async\Future;

// 1. 发起异步子请求(内部路由,无 HTTP 开销)
$future = (new Script('api/task.php'))->async(['id' => 123]);

// 2. 等待结果(支持超时)
$result = $future->await("5s");

// 3. 批量等待
$results = Future::awaitAll([$task1, $task2, $task3], "30s");
$first = Future::awaitAny([$task1, $task2], "10s"); // 竞速等待

结构化并发模式

基于生成器(Generators)实现的高级模式,无需协程运行时或事件循环 :

use function Frankenphp\Async\{race, retry, parallel, throttle};

// 竞速:第一个完成即取消其他
$payment = yield from race([
    (new Script('stripe.php'))->async($cart),
    (new Script('paypal.php'))->async($cart),
], "10s");

// 重试:指数退避
$data = yield from retry(3, 
    fn() => (new Script('api/flaky.php'))->async(), 
    "1s",           // 初始延迟
    2.0             // 退避倍数
);

// 并行:滑动窗口控制并发度
$results = yield from parallel($tasks, concurrency: 5);

// 节流:批量流式处理
foreach (throttle($allIds, 'task.php', batch: 50) as $result) {
    // 每批 50 个任务完成时产出结果
}


性能特征与基准测试

声称的性能数据

根据项目文档,FrankenAsync 实现了 150x+ 的速度提升 ,测试场景为:

  • 任务类型:模拟 I/O 阻塞(usleep)或真实 HTTP 请求(local=0)
  • 并发度:通过 Go 信号量动态调节(默认 threads - 2)
  • 线程规模:已测试支持数百线程(最高 500 线程)

与 ReactPHP/Amp 的对比

维度FrankenAsyncReactPHP / Amp
并发模型操作系统线程并行(真并行)事件循环 + 协程(伪并行)
代码改造零改造,阻塞代码直接运行需重写为异步模式(Promise/Fiber)
运行时依赖FrankenPHP (Go) + ZTS PHP纯 PHP 扩展(uv/libevent)
适用场景CPU 密集型 + I/O 密集型混合纯 I/O 密集型、高并发连接
生态兼容任意现有 PHP 代码、框架需专用异步库(异步 DB、HTTP 客户端)
内存隔离子请求级隔离(线程安全)协程级共享内存
关键差异:ReactPHP/Amp 需要 "异步思维" 重写代码,而 FrankenAsync 采用 "编排而非重写"(Orchestrate, Don't Rewrite) 理念,现有阻塞代码(如 file_get_contents()、DB 查询)无需修改即可并行执行 。


技术限制与先决条件

1. 构建要求

  • ZTS(Zend Thread Safe)PHP:必须使用线程安全版本,通过 static-php-cli 自动构建
  • CGO 环境:需要完整 CGO 编译器标志(CGO_CFLAGS/CGO_LDFLAGS)
  • Go 1.26+:使用最新的 CGO 特性

2. 架构限制

  • Fork 依赖:当前必须依赖 FrankenPHP 的分支版本(添加 Thread(index) API),上游尚未合并
  • Worker 模式冲突:与 FrankenPHP 的 Worker 模式(常驻内存)不同,FrankenAsync 每个子请求都是全新的 PHP 上下文,适用于无状态任务

3. 资源消耗

  • 线程开销:每个并发任务占用一个完整 OS 线程(而非协程的轻量级),高并发场景(>1000)内存消耗显著高于 ReactPHP
  • 信号量瓶颈:FRANKENASYNC_WORKERS 默认设为 threads - 2,避免线程池耗尽

项目结构解析

frankenasync/
├── main.go                 # HTTP 服务器入口,FrankenPHP 初始化
├── asynctask/              # Go 任务管理器核心
│   ├── manager.go          # 信号量、任务生命周期、goroutine 池
│   ├── manager_option.go   # 配置选项
│   └── context.go          # 请求上下文传递
├── phpext/                 # C 扩展 + Go 桥接层(关键技术点)
│   ├── phpext.go           # Go 导出函数(CGO)
│   ├── phpext.c            # PHP 类注册(Script/Future)
│   ├── phpext.h            # PHP 类声明 + arginfo
│   └── phpext_cgo.h        # CGO 桥接头文件
├── examples/
│   ├── lib/async.php       # 结构化并发辅助函数(race/retry/parallel)
│   └── include/task.php    # 阻塞任务示例
└── build/php/              # ZTS PHP 自动构建脚本(static-php-cli)


总结与评估

优势

1. 遗留系统友好:唯一无需重写代码即可实现并行的 PHP 方案 2. 真并行:利用多核 CPU,而非单核事件循环 3. 类型安全:Future 错误通过异常传播(FutureTimeoutException 等),而非回调地狱

局限

1. 实验性质:明确声明非生产框架,API 可能剧烈变动 2. 资源密集:线程模型比协程更重,不适合数万级高并发连接 3. 生态割裂:需要特定的 FrankenPHP Fork 和 ZTS 构建

适用场景

  • 微服务编排:并行调用多个下游服务(商品详情页聚合)
  • 批量数据处理:CPU 密集型任务并行(图片处理、报表生成)
  • 遗留现代化:不愿重写的老代码提速
FrankenAsync 代表了 PHP 并发编程的第三条道路:介于传统多进程(PHP-FPM)和异步协程(ReactPHP)之间的线程级并行,特别适合混合 I/O 和 CPU 密集型、且不愿重写代码的场景。

👍 1

想参与讨论或点赞?登录后使用完整功能

讨论回复(1)

✨

https://github.com/johanjanssens/frankenasync

暂无表态
合作

智谱 GLM-5 已上线

在智谱开放平台 BigModel.cn 打造 AI 应用。新一代旗舰模型 GLM-5 在推理、代码、智能体综合能力达到开源模型 SOTA。

领取 2000万 Tokens