ThinkPHP8.1实现异步任务的核心是剥离耗时操作:①安装think-queue并配redis驱动;②监听器中仅dispatch任务,禁用同步IO;③启动常驻消费者;④可选Swoole协程或pcntl_fork方案。

ThinkPHP8.1中实现异步任务,核心是把耗时操作从主请求生命周期中剥离出去,避免阻塞HTTP响应——用户提交注册后等待3秒才看到“注册成功”,往往不是业务逻辑慢,而是监听器里写了file_get_contents或sleep。
用think-queue走Redis队列(推荐主路径)
第一步:执行命令安装队列扩展
composer require topthink/think-queue
第二步:修改config/queue.php,把driver从'sync'改成'redis'
【必须改,否则dispatch()仍是同步执行】
第三步:在事件监听器里只做轻量封装,例如:
dispatch(new SendWelcomeEmailJob(['user_id' => $user->id, 'email' => $user->email]));
这一步不能写Db::table('log')->insert(...)或curl_exec(),否则整个请求卡住。
立即学习“PHP免费学习笔记(深入)”;
第四步:启动队列消费者
php think queue:work --daemon --memory=128 --delay=3
注意:--daemon参数让进程常驻,不加的话每处理一个任务就退出,无法持续消费。
用Swoole协程(需部署Swoole扩展)
方法一:直接在控制器中启用协程
go(function () {
$client = new Swoole\Coroutine\Http\Client('api.example.com', 443);
$client->set(['timeout' => 5]);
$client->get('/notify');
echo $client->body;
});
这行代码发出请求后立刻往下走,不等响应返回。
方法二:配合事件系统使用
在event listener中调用go()包裹耗时操作,但【必须确认当前运行环境已启用Swoole协程调度器】,否则go()退化为普通函数调用,毫无异步效果。
用pcntl_fork创建子进程(兼容老环境)
if (pcntl_fork() === 0) {
ob_end_clean();
fastcgi_finish_request();
sleep(2);
file_put_contents('/tmp/task.log', 'done');
exit(0);
}
父进程不受影响,继续返回HTTP响应。
注意:fork后必须调用fastcgi_finish_request(),否则Nginx会一直等子进程结束才发响应包。



















