PHP True Async 最近进展以及背后的争议

PHP True Async 最近进展以及背后的争议

PHP True Async 团队还在努力。如果 RFC 通过,将会跟着 PHP 8.6 一起发布。现在RFC 1.6 刚刚进入投票阶段,RFC 1.7 就已经准备就绪。最大的变化是:将 Fiber 作为协程生成器编织进 TrueAsync,并使用显式的 yield(Fiber::suspend)。这一优雅的改动归功于 Bob Weinand(详见 RFC:wiki.php.net/rfc/true_as...

但真正有趣的事情发生在幕后。

原文链接 PHP True Async 最近进展以及背后的争议

WordPress 兼容性争议

在激烈的 TrueAsync RFC 1.6 辩论期间,有一个论点是异步对 PHP 不利,因为它"破坏了 WordPress"。逻辑是:WordPress 依赖全局变量,所以在协程内调用其 API 可能会破坏某些东西。

这是真的吗?作者决定用最糟糕的方式找出答案:将 WordPress 作为有状态应用运行在 TrueAsync 上。这不是第一次尝试------GitHub 上已经有使用 Swoole 实现的项目。困难的部分不在于异步,而在于 WordPress 被设计为在每个请求上"死掉"。重新定义常量、重新包含文件、到处都是全局状态。WordPress 并不是有状态生命周期的理想候选者。

这是否阻止了尝试?当然没有。为了实现这一点,作者构建了一个自定义的 TrueAsync 和 PHP,使全局变量对每个协程唯一,超全局变量对每个 Async\Scope 唯一。

翻译一下:当你在协程内运行代码时,你无法通过全局变量意外地传递数据。作用域本地的超全局变量为接触 $_GET$_POST$_SESSION 等的协程创建了一个沙箱,而不会泄漏到其他请求中。这让你可以在一个进程中运行多个 WordPress 副本,几乎不需要任何更改。可怕吗?绝对的。

第一项工作:一个入口点,初始化 WordPress、数据库连接,并将控制权传递给模板层:

php 复制代码
include_once WP_ROOT . '/wp-config.php';
include_once WP_ROOT . '/wp-settings.php';

ob_start();

// Run WordPress
wp();

$template_loader = ABSPATH . WPINC . '/template-loader.php';
if (file_exists($template_loader)) {
    include $template_loader;
}

$output = ob_get_clean();

$responseHeaders = [
    'Content-Type' => 'text/html; charset=UTF-8',
    'Content-Length' => strlen($output),
    'X-Powered-By' => 'TrueAsync PHP',
];

$success = sendResponse($client, 200, $responseHeaders, $output, $shouldKeepAlive);
ServerStats::$requestHandled++;

// Close MySQL connection to prevent connection leaks
closeMySQLConnection();

return $success ? $shouldKeepAlive : false;

思路很简单:

  • 为每个请求启动一个新的 WordPress。
  • 打开一个唯一的数据库连接。
  • 使用 ob_start/ob_get_clean 捕获 WordPress 输出并发送回去。

要让它工作,你必须从 WordPress 中移除一些 exit/die 调用。之后,你就有了一个可以处理 WordPress 请求的入口点。

但如果我们只初始化 WordPress 一次,然后将其状态克隆到每个请求的协程中呢?如果这样可行,我们可以只克隆 WordPress 的部分内容:当数据不可变时,为多个并发请求重用相同的内存。

添加一个协程来监控进程统计信息,而不会拖慢服务器:

php 复制代码
/**
 * Statistics reporter coroutine
 */
spawn(function(): void {
    while (true) {
        delay(5000);
        $activeCoroutines = count(getCoroutines());
        $activeConnections = ServerStats::$connectionsStarted - ServerStats::$connectionsClosed;

        echo "[Stats] Connections: Accepted=" . ServerStats::$connectionsAccepted .
             " Started=" . ServerStats::$connectionsStarted .
             " Active=" . $activeConnections .
             " Closed=" . ServerStats::$connectionsClosed .
             " | Requests: Total=" . ServerStats::$requestCount .
             " Handled=" . ServerStats::$requestHandled .
             " | Coroutines: " . $activeCoroutines . "\n";
    }
});

令人惊讶的是:大量旧代码就这样工作了。当多个协程访问缓存代码时会出现问题。WordPress 动作处理程序在钩子分发期间有时需要本地状态。需要进行更改。你无法避免它们。

尽管如此......很难忽视有多少代码能够原样运行。并发服务器为任何 PHP 代码提供了以合理代价获得真正性能提升的机会。

并发服务器

PHP 并没有自带并发服务器(除了 Swoole 和基础的 HTTP/1.1 服务器)。如果你无法使用协程,为什么要有协程?用纯 PHP 编写自己的服务器是痛苦的,特别是对于 HTTP/2 或 HTTP/3。

RoadRunner 和 FrankenPHP 团队依靠 Go 来实现性能。让我们看看 TrueAsync 如何适配。

要将 TrueAsync 与 FrankenPHP 集成,PHP 需要一个思维转变:它永远存活,服务器变成了 PHP 驱动的插件:

php 复制代码
<?php

use FrankenPHP\HttpServer;
use FrankenPHP\Request;
use FrankenPHP\Response;

set_time_limit(0);

echo "Starting FrankenPHP TrueAsync HttpServer...\n";

// Register request handler
HttpServer::onRequest(function (Request $request, Response $response) {

    $method = $request->getMethod();
    $uri = $request->getUri();
    $headers = $request->getHeaders();
    $body = $request->getBody();

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

    $responseData = [
        'message' => 'Hello from FrankenPHP TrueAsync!',
        'method' => $method,
        'uri' => $uri,
        'total_coroutines' => count(\Async\get_coroutines()),
        'memory' => round(memory_get_usage(true) / 1024 / 1024, 2),
        'timestamp' => date('Y-m-d H:i:s'),
        'headers_count' => count($headers),
        'body_length' => strlen($body),
    ];

    $response->write(json_encode($responseData, JSON_PRETTY_PRINT));
    $response->end();
});

echo "Request handler registered. Event loop is running.\n";
// Script stays loaded, event loop handles requests

现在我们可以构建跳过每次请求都重新初始化所有内容的应用: 有状态应用

我们可以在并发协程之间共享这些预热的服务:

TrueAsync 为高性能 PHP 应用打开了大门,通过混合有状态架构和并发处理,可以与其他语言竞争。

下一步是什么?

RFC 1.7 即将进入讨论阶段,更多实验正在进行中。例如:使用 TrueAsync 和 FrankenPHP 运行 Laravel:github.com/true-async/...

相关推荐
程序员码歌2 小时前
短思考第264天,每天复盘5分钟,胜过你盲目努力1整年(2)
前端·后端·ai编程
Victor3562 小时前
Hibernate(3)Hibernate的优点是什么?
后端
Victor3562 小时前
Hibernate(4)什么是Hibernate的持久化类?
后端
JaguarJack2 小时前
PHP True Async 最近进展以及背后的争议
后端·php
想不明白的过度思考者4 小时前
Spring Boot 配置文件深度解析
java·spring boot·后端
WanderInk9 小时前
刷新后点赞全变 0?别急着怪 Redis,这八成是 Long 被 JavaScript 偷偷“改号”了(一次线上复盘)
后端
吴佳浩11 小时前
Python入门指南(七) - YOLO检测API进阶实战
人工智能·后端·python
廋到被风吹走11 小时前
【Spring】常用注解分类整理
java·后端·spring