PHP 中三种异步模式对比:FPM 同步、Swoole 协程与消息队列
PHP-FPM模式 (传统同步阻塞)
Redis队列实现的 “ 任务异步 ”
Swoole协程 + thinkPHP 的 “ 运行时异步 ” (协程/事件驱动)
FPM是什么?
- FPM 是php常用的web服务器接口,用于处理HTTP请求。它工作在Master-Worker架构下:
Master 进程:管理子进程(启动、回收、监听信号)
Worker 子进程:每个子进程独立处理一个HTTP请求,请求结束就释放(或复用)
每个Worker 进程一次只能处理一个请求,且必须等该请求完全执行完才可以处理下一个
提示
整个过程中: 如果register.php 调用了Redis、MYSQL、外部API,每一步都会阻塞当前Worker 进程 直到脚本执行完毕(或者超时),该Worker 才能被回收或复用。
- 案例:
// 1. 写入数据库(耗时 50ms)
$pdo->exec("INSERT INTO users (name) VALUES ('Alice')");
// 2. 发送欢迎邮件(调用 SMTP,耗时 800ms)
mail('alice@example.com', 'Welcome!', 'Hi Alice!');
echo "注册完成!"; // 用户需等待850 毫秒才能看到结果重要
- 用户必须等850毫秒 才收到响应
- 这个期间,FPM Worker 完全被占用,无法处理其他请求
- 如果并发100人注册,就需要至少100个FPM 进程,内存压力大
- FPM 同步阻塞模型的根本局限
Redis队列的异步:任务的解耦(架构级异步)
- 核心思想:把耗时操作 “甩出去”
- 这种“异步”适用于解耦耗时任务(发邮件、生成报表、视频转码),但不提升单次请求内的 I/O 并发能力。
不等任务耗时完成,把任务写入Redis队列,立即返回。
由另一个CLI进程(Worker)在后台慢慢处理 (相当于一个单独的服务)
- 案例:
Web 层(FPM 中)—— 快速响应
// register.php (FPM)
$userId = $pdo->lastInsertId(); // 假设已插入用户
// 投递两个异步任务
Redis::lpush('job_queue', json_encode(['job' => 'send_welcome_email', 'user_id' => $userId]));
Redis::lpush('job_queue', json_encode(['job' => 'risk_check', 'user_id' => $userId]));
echo json_encode(['code' => 200, 'msg' => '注册成功']); // 立即返回!
相当于启动一个服务,队列消费者服务(进行实时检测)
// worker.php (在终端运行: php worker.php)
while (true) {
// 阻塞等待任务(BRPOP)
$job = Redis::brpop('job_queue', 0); // 0 表示无限等待
$data = json_decode($job[1], true);
switch ($data['job']) {
case 'send_welcome_email':
sleep(1); // 模拟发邮件耗时
echo " 邮件已发送给 {$data['user_id']}\n";
break;
case 'risk_check':
file_get_contents("https://risk-api/check?user={$data['user_id']}");
echo "🛡风控检查完成\n";
break;
}
}Swoole 异步 (运行时协程并发)
- 核心思想:在单次请求内,并发执行多个I/O
- Swoole 是PHP的常驻内存、事件驱动、协程化引擎,它让PHP具备GO 或Node.js 的高并发能力
协程(Coroutine) —— 在 I/O 等待时自动“挂起”,切换去执行其他任务,I/O 完成后再“恢复”。

重要
所有 I/O 几乎同时发出,总耗时 ≈ 最慢的那个操作,而非三者之和!
- 案例:
// app/controller/User.php
use Swoole\Coroutine;
use think\facade\Db;
public function register()
{
// 启动协程上下文(TP6+Swoole 自动支持)
$userId = Db::name('users')->insertGetId(['name' => 'Bob']);
// 并发执行三个异步任务
Coroutine::create(function () use ($userId) {
// 使用 Swoole 协程 Redis
$redis = new \Swoole\Coroutine\Redis();
$redis->connect('127.0.0.1', 6379);
$redis->set("user:{$userId}:profile", json_encode(['name' => 'Bob']));
});
Coroutine::create(function () use ($userId) {
// 使用 Swoole 协程 HttpClient
$client = new \Swoole\Coroutine\Http\Client('risk-api', 443, true);
$client->get("/check?user={$userId}");
$client->close();
});
// 注意:发邮件若用 SMTP 协程客户端也可并行
// 此处简化,假设邮件服务也支持协程
return json(['code' => 200, 'msg' => '注册成功', 'time' => microtime(true)]);
}重要
必须使用 Swoole 提供的协程客户端(如 Swoole\Coroutine\Redis、Swoole\Coroutine\Http\Client),否则会退化为阻塞!
总结
- 简单项目、低并发 -> 用FPM
- 有耗时任务、不要求实时 -> Redis队列
- 高并发、低延迟、实时交互 -> Swoole
版权所有
版权归属:念宇
