PHP 异步编程简介
传统上,PHP 被认为是一种同步语言:每个请求按顺序执行,在输入输出操作(例如读取文件或数据库查询)完成之前会阻塞执行。然而,随着高负载 Web 应用和微服务的日益流行,需要在不扩展进程数量的情况下处理数万个并发连接。异步编程通过允许在单个线程内并发执行操作来解决这个问题。
在本教程中,我们将分析 PHP 中异步的工作原理、可用的工具以及如何正确构建非阻塞应用程序。您将了解生成器、协程、ReactPHP、Amp 和 Swoole 库,以及新的 Fibers 扩展。
异步基础:Event Loop 和非阻塞输入输出
异步编程基于 Event Loop(事件循环)的概念。这是一个无限循环,等待并处理事件:网络请求、输入输出操作完成、定时器。代码不是等待操作完成,而是注册一个回调(callback),当数据准备好时执行该回调。这样可以避免浪费 CPU 时间等待。
PHP 传统上使用阻塞函数(例如 file_get_contents() 或 sleep())。为了异步工作,需要用非阻塞的替代品替换它们。以下是一个使用 ReactPHP 库的简单示例:
<?php
require 'vendor/autoload.php';
use React\\EventLoop\\Factory;
use React\\Promise\\Promise;
$loop = Factory::create();
// 异步读取文件
$promise = new Promise(function ($resolve, $reject) use ($loop) {
$loop->addTimer(0.001, function () use ($resolve) {
$content = file_get_contents('/etc/hosts');
$resolve($content);
});
});
$promise->then(function ($data) {
echo "已读取: " . strlen($data) . " 字节";
});
echo "此代码将立即执行,无需等待文件读取";
$loop->run();
?>
在此示例中,addTimer 注册了一个将在循环的下一个 tick 中执行的任务。Event Loop 不会被阻塞,我们立即看到消息,而读取结果稍后出现。
生成器和协程:控制执行流程
生成器(yield)出现在 PHP 5.5 中,允许暂停函数执行并返回中间值。在此基础上构建了 协程——轻量级线程,可以自愿交出控制权。与 Event Loop 结合,生成器允许编写看起来像同步但异步执行的代码。
考虑一个使用 Amp 库的示例,该库使用协程:
<?php
require 'vendor/autoload.php';
use Amp\\Loop;
use Amp\\Delayed;
// 异步协程
$coroutine = function () {
echo "协程开始";
// 暂停 1 秒而不阻塞
yield new Delayed(1000);
echo "已过 1 秒";
yield new Delayed(500);
echo "又过了 0.5 秒";
};
Loop::run(function () use ($coroutine) {
// 启动协程
$result = yield from $coroutine();
echo "协程已完成";
});
?>
在此代码中,yield new Delayed(1000) 暂停协程的执行,将控制权交还给 Event Loop。1 秒后,执行从同一位置恢复。这使得可以同时处理数千个这样的协程,而无需创建新进程。
用于异步 PHP 的库和工具
在 PHP 中处理异步有多个成熟的库。让我们看看三个最流行的。
ReactPHP
ReactPHP 是最古老且最知名的库。它提供 Event Loop、Promise(类似于 JavaS