找回密码
 立即注册
首页 业界区 业界 PHP True Async 最近进展以及背后的争议

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

阙忆然 2 小时前
PHP True Async 最近进展以及背后的争议

PHP True Async 团队还在努力。如果 RFC 通过,将会跟着 PHP 8.6 一起发布。现在RFC 1.6 刚刚进入投票阶段,RFC 1.7 就已经准备就绪。最大的变化是:将 Fiber 作为协程生成器编织进 TrueAsync,并使用显式的 yield(Fiber::suspend)。这一优雅的改动归功于 Bob Weinand(详见 RFC:https://wiki.php.net/rfc/true_async#fiber_support)。
但真正有趣的事情发生在幕后。
原文链接 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、数据库连接,并将控制权传递给模板层:
  1. include_once WP_ROOT . '/wp-config.php';
  2. include_once WP_ROOT . '/wp-settings.php';
  3. ob_start();
  4. // Run WordPress
  5. wp();
  6. $template_loader = ABSPATH . WPINC . '/template-loader.php';
  7. if (file_exists($template_loader)) {
  8.     include $template_loader;
  9. }
  10. $output = ob_get_clean();
  11. $responseHeaders = [
  12.     'Content-Type' => 'text/html; charset=UTF-8',
  13.     'Content-Length' => strlen($output),
  14.     'X-Powered-By' => 'TrueAsync PHP',
  15. ];
  16. $success = sendResponse($client, 200, $responseHeaders, $output, $shouldKeepAlive);
  17. ServerStats::$requestHandled++;
  18. // Close MySQL connection to prevent connection leaks
  19. closeMySQLConnection();
  20. return $success ? $shouldKeepAlive : false;
复制代码
思路很简单:

  • 为每个请求启动一个新的 WordPress。
  • 打开一个唯一的数据库连接。
  • 使用 ob_start/ob_get_clean 捕获 WordPress 输出并发送回去。
要让它工作,你必须从 WordPress 中移除一些 exit/die 调用。之后,你就有了一个可以处理 WordPress 请求的入口点。
但如果我们只初始化 WordPress 一次,然后将其状态克隆到每个请求的协程中呢?如果这样可行,我们可以只克隆 WordPress 的部分内容:当数据不可变时,为多个并发请求重用相同的内存。
添加一个协程来监控进程统计信息,而不会拖慢服务器:
  1. /**
  2. * Statistics reporter coroutine
  3. */
  4. spawn(function(): void {
  5.     while (true) {
  6.         delay(5000);
  7.         $activeCoroutines = count(getCoroutines());
  8.         $activeConnections = ServerStats::$connectionsStarted - ServerStats::$connectionsClosed;
  9.         echo "[Stats] Connections: Accepted=" . ServerStats::$connectionsAccepted .
  10.              " Started=" . ServerStats::$connectionsStarted .
  11.              " Active=" . $activeConnections .
  12.              " Closed=" . ServerStats::$connectionsClosed .
  13.              " | Requests: Total=" . ServerStats::$requestCount .
  14.              " Handled=" . ServerStats::$requestHandled .
  15.              " | Coroutines: " . $activeCoroutines . "\n";
  16.     }
  17. });
复制代码
令人惊讶的是:大量旧代码就这样工作了。当多个协程访问缓存代码时会出现问题。WordPress 动作处理程序在钩子分发期间有时需要本地状态。需要进行更改。你无法避免它们。
尽管如此……很难忽视有多少代码能够原样运行。并发服务器为任何 PHP 代码提供了以合理代价获得真正性能提升的机会。
并发服务器

PHP 并没有自带并发服务器(除了 Swoole 和基础的 HTTP/1.1 服务器)。如果你无法使用协程,为什么要有协程?用纯 PHP 编写自己的服务器是痛苦的,特别是对于 HTTP/2 或 HTTP/3。
RoadRunner 和 FrankenPHP 团队依靠 Go 来实现性能。让我们看看 TrueAsync 如何适配。
要将 TrueAsync 与 FrankenPHP 集成,PHP 需要一个思维转变:它永远存活,服务器变成了 PHP 驱动的插件:
[code]

相关推荐

您需要登录后才可以回帖 登录 | 立即注册