fastcgi_finish_request()
是 PHP-FPM (FastCGI Process Manager) 提供的特殊函数,用于在不关闭 PHP 进程的情况下,立即将响应发送给客户端。
Nginx/Apache PHP-FPM PHP脚本
│ │ │
├─── 请求 ──────────→│ │
│ ├─── 执行 ─────────→│
│ │ │ 处理业务逻辑
│ │ │ 生成响应内容
│ │ │
│ │ │ fastcgi_finish_request()
│ │←─── 响应 ─────────┤
│←─── 响应 ──────────┤ │
│ │ │ 继续执行后续代码
│ 用户已收到响应 │ │ 调用第三方API
│ │ │ 发送邮件等
│ │ │ 处理完毕
│ │ │
<?php
echo "开始处理...";
// 1. 刷新输出缓冲区
ob_flush();
flush();
// 2. 调用 fastcgi_finish_request()
fastcgi_finish_request();
// ===== 分界线 =====
// 用户已经收到响应,但 PHP 继续执行
sleep(10); // 用户不会等待这10秒
echo "这个不会发送给用户";
刷新所有输出缓冲区
关闭与客户端的连接
PHP 进程继续运行
资源保持
// Paper.php
public function submit() {
// 1. 保存成绩
saveGrade($data); // 100ms
// 2. 调用工行接口
callIcbcApi(); // 3000ms (3秒)
// 3. 返回响应
$this->success('提交成功');
}
用户等待时间: 100ms + 3000ms = 3100ms ❌
// Paper.php
public function submit() {
// 1. 保存成绩
saveGrade($data); // 100ms
// 2. 立即返回响应
$this->success('提交成功');
// 3. 结束FastCGI响应
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
// 4. 后台调用工行接口(用户已收到响应)
callIcbcApi(); // 3000ms (用户不用等)
}
用户等待时间: 100ms ✅
// Paper.php
public function submit() {
// 1. 保存成绩
saveGrade($data); // 100ms
// 2. 写入队列
addQueue($data); // 5ms
// 3. 返回响应
$this->success('提交成功');
// 4. 异步处理队列
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
processQueue(); // 用户不用等
}
用户等待时间: 100ms + 5ms = 105ms ✅✅✅
public function order() {
// 创建订单
$order = createOrder($data);
// 返回给用户
$this->success('订单创建成功', $order);
// 异步执行
fastcgi_finish_request();
// 调用支付接口(慢)
notifyPaymentGateway($order);
// 发送短信通知(慢)
sendSMS($order->phone);
// 更新统计数据(慢)
updateStatistics();
}
public function action() {
// 业务处理
$result = doSomething();
// 返回响应
$this->success('操作成功', $result);
// 异步记录详细日志
fastcgi_finish_request();
// 写入详细日志(可能很慢)
writeDetailLog($result);
// 上报到监控系统
reportToMonitor($result);
}
public function update() {
// 更新本地数据
updateLocalData($data);
// 返回成功
$this->success('更新成功');
// 异步同步到其他系统
fastcgi_finish_request();
// 同步到ES
syncToElasticsearch($data);
// 同步到Redis
syncToRedis($data);
// 推送到消息队列
pushToQueue($data);
}
// 必须先检查函数是否存在
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} else {
// CLI 模式或 Apache mod_php 下不可用
// 需要其他方案
}
// 如果使用了 Session,需要先关闭
session_write_close();
// 然后再调用
fastcgi_finish_request();
// 否则 Session 文件会一直被锁定
// 响应后继续使用数据库
fastcgi_finish_request();
// 数据库连接仍然可用
Db::name('log')->insert($data);
// 但要注意连接超时问题
fastcgi_finish_request();
// 此后的异常不会被用户看到
try {
dangerousOperation();
} catch (Exception $e) {
// 必须记录日志,用户已经看不到错误了
Log::error('后台任务失败: ' . $e->getMessage());
}
// PHP 脚本执行时间限制仍然有效
fastcgi_finish_request();
// 如果后续操作耗时很长,需要设置
set_time_limit(300); // 5分钟
// 或者在 php.ini 中设置 max_execution_time
addons/exam/controller/Paper.php
private function processIcbcQueueAsync()
{
// 使用fastcgi_finish_request()立即返回响应给用户
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
// 后台处理队列
$this->processIcbcQueue();
}
1. 用户交卷
↓
2. 保存成绩到数据库 (100ms)
↓
3. 写入积分队列表 (5ms)
↓
4. 返回成功响应给用户
↓
5. 调用 fastcgi_finish_request() ← 用户已收到响应
↓
6. 处理队列,调用工行接口 (3000ms) ← 用户不用等
↓
7. 更新队列状态
↓
8. 记录日志
↓
9. 脚本结束
优点: 简单
缺点: 用户等待时间长
exec('php task.php > /dev/null 2>&1 &');
优点: 完全异步
缺点: 创建新进程,开销大,不好管理
优点: 专业的异步方案
缺点: 需要额外的服务,复杂度高
优点: 简单可靠,无需额外服务
缺点: 处理能力受限于数据库
我们的方案: ✅ 方案4(数据库队列 + fastcgi_finish_request())
REQUEST_BEGIN
↓
PARAMS (请求参数)
↓
STDIN (输入数据)
↓
处理脚本
↓
STDOUT (输出数据) ← fastcgi_finish_request() 在这里关闭连接
↓
REQUEST_END ← 但脚本继续运行
[PHP-FPM Master]
├─ [Worker 1] ← 处理请求1
├─ [Worker 2] ← 处理请求2
├─ [Worker 3] ← 调用了 fastcgi_finish_request()
│ 用户已收到响应
│ 但进程仍在执行后续代码
└─ [Worker 4] ← 处理请求4
不是真正的多线程或异步
适用场景
不适用场景
最佳实践
// 1. 快速响应用户
fastcgi_finish_request(); // 立即返回
// 2. 有队列保证可靠性
addQueue($data); // 数据不丢失
// 3. 有定时任务兜底
php think icbc_queue // 处理失败任务
// 4. 有日志可追踪
writeLog($result); // 记录所有操作
这是一个生产级的异步处理方案! 🎉