PHP 7 是否默认异步处理请求?

Does PHP 7 handle requests asynchronously by default?

在过去的 3.5 年里,我一直在写作 PHP,我喜欢它。当最近 PHP v/s Nodejs 的热潮传来时,最引人注目的一件事就是 Nodejs 默认实现了非阻塞(或异步)I/O 而 PHP 没有。

然而,当我最近尝试在 PHP 中实现一个简单的异步请求路由器(使用 exec() 函数)并尝试将我的代码与标准(应该是同步请求处理程序脚本),我注意到一些东西 st运行ge。

在进一步解释之前,我希望您看一下下面的代码。

sync.php应该搁置所有进一步请求直到当前请求完成处理的请求

<?php
    $a = $argv[1];
    echo "starting: ".$a;
    $ar = array();
    $sum = 0;
    $ul = 15000;
    for($i = 1; $i < $ul; $i++){
        $ar[$i] = $i;
    }
    for($i = 1; $i < $ul;  $i++){
        for($j = 1; $j < $ul; $j++){
            $sum += $ar[$j];
        }
    }
    echo "\n\nDone: ".$a."\nResult: ".$sum."\n";
?>

我做了两种测试。

首先,我 运行 在我的计算机上运行了四个终端实例并执行了:

php sync.php 1  

同时全部

接下来,我打开我的 XAMPP 服务器,通过 serveo 公开我的本地主机,并同时从三个不同的设备向这个脚本发出三个请求。

结果出乎意料。

无论我发送多少请求,所有请求(似乎)都会并行执行,结果会(几乎)同时显示在所有终端 windows/browser 选项卡上(考虑到 1手动 运行 每个终端中的命令 window) 时发生 -2 秒延迟。

这与 PHP 实现阻塞 I/O 的事实不符(我就是这么想的)。相反,我期望的是获得

的总突发时间
n * 4 seconds

其中 n 是处理每个请求所花费的时间(在我的笔记本电脑上大约 8 秒)。

那么,为什么请求会被异步处理??是因为 for 循环 是异步执行的函数之一,还是它与阻塞 I/O 模型根本没有任何关系??

单个 PHP 进程 正在阻塞。例如,您可以使用 built-in development server,这将启动一个单进程网络服务器。根据那里的注释:

The web server runs only one single-threaded process, so PHP applications will stall if a request is blocked.

由于显而易见的原因,如果您有成千上万的人同时访问您的网站,这会带来问题,因此有一个简单的解决方案 - 多个 PHP 个进程。这可以通过多种方式设置 - Apache 将 fire up multiple webserver workers, or you can run PHP-FPM ("FastCGI Process Manager").

只要他们是空闲的工作人员,他们就会并行处理传入的请求。

您误解了正在讨论的有关阻止的内容 I/O。

每次您从命令行 运行 一个 PHP 脚本时,它会在一个完全独立的进程中执行,该进程不知道任何其他 PHP 脚本 运行宁在机器上。您可以多次执行它,原因与您可以同时 运行 Web 浏览器和文本编辑器完全相同 - OS 正在一个或多个处理器内核上调度进程。

在 Web 服务器示例中,它稍微复杂一些,但适用相同的原则:您向 Web 服务器发出的每个请求都会创建一个新进程,或者在进程中创建一个新线程,并且 PHP 脚本 运行s 在里面。

人们正在讨论的有关阻止 I/O 的内容完全不同:当您在 代码中访问 外部内容时,例如获取 Web 内容使用 HTTP 请求。想象一下这个循环,使用一个想象的 HTTP 库:

foreach ( $urls as $url ) {
    $results[] =  $httpClient->fetchUrl($url);
}

如果使用内置 PHP 功能编写,这将 停止 运行ning 并等待远程服务器响应 每次环形。因此,对于 10 个请求,每个请求花费一秒钟,完成循环需要 10 秒。

"Non-blocking I/O" 意味着实现像 fetchUrl 这样的功能,以便它们立即 return,即使 HTTP 响应还没有返回。这样,您可以一次 运行 大量 HTTP 请求:在示例循环中,您将在 1 秒后获得所有 10 个响应。然后您将有一些方法来获得结果,例如 "promise"。

可以在 PHP 中实现此功能,但没有默认情况下以用户友好的方式执行此操作的本机函数。

更复杂的是,系统可以为 input 实现此功能:每当您在等待外部的东西时,您可以检查是否有新的 Web 请求进入,然后开始处理那个。这可以有效地模拟多个线程,这就是 NodeJS 能够处理每个请求一个线程的大量流量的方式。