非阻塞模式利用 Socket 事件消息機制,使 Server 與 Client 通信處於異步狀態。當 PHP 作為後端處理耗時任務時,為快速響應前端請求,可採用以下七種方案實現「即發即忘」或異步處理。
立即結束會話返回響應,PHP 進程繼續在後台執行:
<?php
echo "program start.";
file_put_contents('log.txt', 'start-time:' . date('Y-m-d H:i:s'), FILE_APPEND);
fastcgi_finish_request(); // 關鍵:立即返回給瀏覽器
sleep(1);
echo 'debug...'; // 瀏覽器不會收到此輸出
file_put_contents('log.txt', 'start-proceed:' . date('Y-m-d H:i:s'), FILE_APPEND);
sleep(10);
file_put_contents('log.txt', 'end-time:' . date('Y-m-d H:i:s'), FILE_APPEND);
效果:瀏覽器只看到 "program start.",但日誌文件記錄完整三個時間點。
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) die('error fsockopen');
stream_set_blocking($fp, 0); // 關鍵:設置非阻塞
$http = "GET /save.php HTTP/1.1\r\n";
$http .= "Host: www.example.com\r\n";
$http .= "Connection: Close\r\n\r\n";
fwrite($fp, $http);
fclose($fp);
$cmh = curl_multi_init(); $ch1 = curl_init(); curl_setopt($ch1, CURLOPT_URL, "https://localhost:6666/child.php"); curl_multi_add_handle($cmh, $ch1); curl_multi_exec($cmh, $active); echo "End\n"; // 立即執行,不等待 child.php 完成
將任務數據寫入 Redis 列表/流,後台 Worker (crontab/Supervisor) 消費處理。大流量架構常見模式。
$cmd = 'nohup php ./processd.php $someVar >/dev/null &'; `$cmd`; // 反引號執行系統命令
極端情況下可用,但效率較低,不推薦常規使用。
參考國外大神文章:PHP 協程合作式多任務。利用生成器實現用戶態協程調度。
if (($pid = pcntl_fork()) == 0) {
child_func(); // 子進程執行耗時任務
} else {
father_func(); // 主進程繼續
}
echo "Process " . getmypid() . " get to the end.\n";
function father_func() {
echo "Father pid is " . getmypid() . "\n";
}
function child_func() {
sleep(6);
echo "Child process exit pid is " . getmypid() . "\n";
exit(0);
}
注意:需安裝 pcntl 擴展,易產生殭屍進程,需配合 pcntl_signal 處理 SIGCHLD。
| 方案 | 適用場景 | 複雜度 | 依賴 |
|---|---|---|---|
| fastcgi_finish_request | 單請求後台處理 | 低 | PHP-FPM |
| fsockopen/cURL | 異步調用外部服務 | 中 | 無/擴展 |
| Gearman | 分布式任務隊列 | 高 | 擴展+服務端 |
| Swoole | 高性能異步服務 | 中 | 擴展 |
| Redis隊列 | 解耦異步處理 | 中 | Redis |
| 系統命令 | 極簡場景 | 低 | 無 |
| pcntl_fork | 並行計算密集型 | 高 | 擴展 |
選型建議:簡單場景用 fastcgi_finish_request 或 Redis 隊列;高性能長連接服務首選 Swoole;分布式任務用 Gearman。