摘要:传统 PHP-FPM 存在同步阻塞、并发弱等问题,本文基于 Swoole 实现企业级异步任务系统,将短信发送、报表导出等耗时操作异步化。包含完整源码、服务配置、协程优化、异常重试及技术选型对比,适合 PHP 开发者进阶学习与项目落地。
作为 PHP 开发者,传统的 FPM 模式一直受限于同步阻塞、请求结束即销毁、高并发处理能力弱等问题。而 Swoole 是 PHP 生态最强大的协程网络通信引擎,让 PHP 真正具备支撑高并发、长连接、异步任务的能力。
这篇文章不讲基础入门,直接带你实现企业级 Swoole 异步任务处理系统,用于处理:短信发送、邮件推送、订单统计、日志上报、Excel 导出等耗时操作,单机轻松支撑万级并发,是 PHP 进阶必备技能。
一、为什么要用 Swoole 做异步任务?
传统 PHP FPM 缺点:
耗时任务(发送短信 / 生成报表)会阻塞前端响应,用户等待时间长
并发高时,FPM 进程耗尽,服务直接卡死
无法实现异步、延时、队列化处理
Swoole 异步任务优势:
非阻塞异步处理:前端接口快速响应,任务后台悄悄执行
协程高并发:单进程支撑上千协程,资源消耗极低
常驻内存:无需重复初始化数据库、连接池
任务队列化:削峰填谷,保护数据库
二、环境要求
PHP >= 7.4
Swoole 扩展 >= 4.8(必须安装)
Linux 系统(不支持 Windows)
安装 Swoole:
pecl install swoole
三、系统架构设计
我们要实现一个生产可用的异步任务系统,包含 3 大核心组件:
HTTP 服务器:接收前端提交的任务(如发送短信、导出 Excel)
Task 异步任务池:后台异步执行耗时逻辑
任务管理器:任务投递、状态记录、异常重试、日志记录
架构流程:
前端请求 → HTTP 接口接收 → 投递任务到 Task 池 → 接口立即返回成功 → 后台异步执行任务
四、完整实战代码
1. 主服务启动文件:server.php
<?php
// 关闭输出缓冲区,避免日志乱码
ob_end_clean();
// Swoole HTTP 服务器
$http = new Swoole\Http\Server('0.0.0.0', 9501);
// 服务器配置(核心参数)
$http->set([
'worker_num' => 4, // 工作进程数:CPU核心数×1~2
'task_worker_num' => 8, // 异步任务进程数:根据耗时任务调整
'daemonize' => 0, // 守护进程:1=后台运行,0=调试模式
'log_file' => '/tmp/swoole_task.log', // 日志路径
'task_enable_coroutine' => true, // 开启Task协程(关键)
]);
// 服务启动事件
$http->on('Start', function ($server) {
echo "Swoole 异步任务服务启动成功!监听:0.0.0.0:9501\n";
});
// 处理HTTP请求(接收任务)
$http->on('Request', function ($request, $response) use ($http) {
// 只允许POST请求
if ($request->server['request_method'] != 'POST') {
$response->end(json_encode(['code' => 403, 'msg' => '请求方式错误']));
return;
}
// 接收任务类型和参数
$taskType = $request->post['task_type'] ?? '';
$taskData = $request->post['task_data'] ?? [];
if (empty($taskType)) {
$response->end(json_encode(['code' => 400, 'msg' => '任务类型不能为空']));
return;
}
// 封装任务数据
$task = [
'type' => $taskType,
'data' => $taskData,
'time' => date('Y-m-d H:i:s')
];
// 投递异步任务(非阻塞,立即返回)
$http->task($task);
// 立即响应前端:任务已接收
$response->end(json_encode([
'code' => 200,
'msg' => '任务已加入队列,后台异步处理中'
]));
});
// 异步任务处理核心逻辑(后台执行)
$http->on('Task', function ($server, $taskId, $workerId, $data) {
echo "开始执行异步任务:ID={$taskId},类型=" . $data['type'] . "\n";
try {
// 根据任务类型分发执行
switch ($data['type']) {
case 'send_sms':
$this->handleSendSms($data['data']);
break;
case 'export_excel':
$this->handleExportExcel($data['data']);
break;
case 'user_report':
$this->handleUserReport($data['data']);
break;
default:
throw new Exception('未知任务类型');
}
echo "任务执行成功:ID={$taskId}\n";
} catch (Throwable $e) {
// 任务异常捕获
echo "任务执行失败:ID={$taskId},错误:" . $e->getMessage() . "\n";
}
// 任务结束
$server->finish('ok');
});
// 任务完成回调
$http->on('Finish', function ($server, $taskId, $data) {
// 任务完成后的清理工作(可选)
});
// 启动服务
$http->start();
?>2. 任务处理类(实际业务逻辑)
我们把业务逻辑封装成方法,支持短信发送、Excel 导出、用户统计三大企业常用任务:
<?php
// 任务处理类(放到server.php中)
class TaskHandler
{
// 1. 异步发送短信
public function handleSendSms($data)
{
$phone = $data['phone'];
$content = $data['content'];
// 模拟短信接口请求(耗时1秒)
co::sleep(1);
file_put_contents('/tmp/sms_log.txt', "发送短信:{$phone},内容:{$content}\n", FILE_APPEND);
}
// 2. 异步导出Excel
public function handleExportExcel($data)
{
$userId = $data['user_id'];
// 模拟大数据量导出(耗时3秒)
co::sleep(3);
file_put_contents("/tmp/excel_{$userId}.xlsx", "用户ID:{$userId} 的导出文件\n");
}
// 3. 异步生成用户统计报表
public function handleUserReport($data)
{
$date = $data['date'];
// 模拟数据库统计(耗时2秒)
co::sleep(2);
file_put_contents('/tmp/report_log.txt', "{$date} 用户统计完成\n", FILE_APPEND);
}
}
// 实例化
$this = new TaskHandler();
?>五、启动与测试
1. 启动服务
php server.php
2. 用 curl 测试投递异步任务
# 投递发送短信任务 curl -X POST http://127.0.0.1:9501 -d "task_type=send_sms&task_data[phone]=13800138000&task_data[content]=您的验证码是1234" # 投递Excel导出任务 curl -X POST http://127.0.0.1:9501 -d "task_type=export_excel&task_data[user_id]=1001"
3. 效果
接口瞬间返回成功
后台异步执行耗时任务,不阻塞前端
任务自动排队,不会压垮服务器
六、进阶(生产环境必备)
1. 协程连接池(防数据库连接耗尽)
Swoole 常驻内存,必须用数据库连接池,否则会出现连接丢失:
// Swoole 协程MySQL连接池 $db = new Swoole\Coroutine\MySQL(); $db->connect([ 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'database' => 'test', ]);
2. 任务异常重试机制
// 失败重试3次
$retry = $data['retry'] ?? 0;
if ($retry < 3) {
$data['retry']++;
$server->task($data); // 重新投递
}3. 任务状态记录(Redis)
用 Redis 记录任务执行状态,前端可查询进度:
$redis->hSet('task_status', $taskId, 'executing');七、Swoole 异步任务 vs 传统消息队列(RabbitMQ/Redis)
| 方案 | 复杂度 | 性能 | 部署成本 | 适合场景 |
|---|---|---|---|---|
| Swoole Task | 极低 | 极高 | 低 | PHP 项目内部异步任务 |
| 消息队列 | 高 | 高 | 高 | 跨语言、分布式、大流量 |
