作为一名同步 PHP 开发者,我对异步 PHP 的心得体会

我使用 Laravel 很多年了。我一直使用同步 PHP。一个请求进来,一个进程运行,然后返回一个响应。我从未觉得需要异步。

后来我读到了关于新的 PHP 8.6 Polling API 的内容。它改变了我对 PHP 如何处理任务的看法。

以下是异步工作原理的详细解析。

阻塞 I/O 的问题

当你调用一个 API 时,你的代码会进入等待状态。 示例:$response = Http::get('https://api.example.com');

如果该 API 需要 300ms,你的 PHP 进程在 300ms 内将无所事事。它处于休眠状态,占用着内存并占据了一个 worker 插槽。如果所有的 worker 都在休眠,你的服务器就会停止接收新请求。

异步解决方案

异步允许你在那 300ms 内做其他工作。与其等待,不如运行其他任务。

但你如何知道数据何时到达呢?这就是内核(kernel)发挥作用的地方。

轮询(Polling)的演进

1. select()

PHP 从 PHP 4 版本起就有了 stream_select()。它会询问内核:“这些套接字(sockets)上有任何就绪的数据吗?” 问题在于重新扫描带来的开销(rescan tax)。如果你有 10,000 个连接,每次都必须将整个列表发送给内核。这既缓慢又会触及限制。

2. epoll / kqueue

这是一种内核特性,而非语言特性。Linux 使用 epoll,macOS 使用 kqueue。 内核不再扫描整个列表,而是维护一个就绪列表(ready-list)。它只会告诉你哪些特定的套接字有数据。这可以扩展到数千个连接而不会产生额外成本。

3. Fibers (PHP 8.1)

Fibers 允许你在调用栈中的任何地方暂停一个函数。 Fiber 不会自动唤醒。它就像一段暂停的 YouTube 视频,必须有人调用 $fiber->resume() 才能让它重新播放。

缺失的一环:PHP 8.6

异步 I/O 需要三个部分: • 暂停:Fibers(现已包含在 PHP 核心中) • 决策:Event Loop(纯 PHP 代码) • 感知:Kernel Polling(缺失的一环)

直到现在,PHP 一直缺乏一种原生的方式,在不使用旧工具或 C 扩展的情况下“感知”哪个套接字已就绪。

PHP 8.6 填补了这一空白。它为核心引入了原生的 Polling API。它会在 Linux 上自动使用 epoll,在 Mac 上使用 kqueue

宏观视角

异步并非魔法。Event loop 本质上就是一段 PHP 代码,用于决定何时对 Fiber 调用 resume()

Fibers 提供了暂停的能力。 epoll 提供了感知何时恢复运行的智能。

如果你目前只使用同步 PHP,那么今天你不需要更改你的 Laravel 应用。但理解这一模型会让 ReactPHP 或 Amp 等异步库变得更容易掌握。

去构建,而不仅仅是使用。亲自运行代码,看看它是如何工作的。

来源:https://dev.to/alamriku/sync-php-developer-hisebe-async-php-bujhte-giye-yaa-shikhlaam-fibers-epoll-aar-php-86-462j