首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 非阻塞/異步編程全方位解析:七大方案對比與實戰代碼

     編輯:PHP 相關知識

什麼是非阻塞模式?

非阻塞模式利用 Socket 事件消息機制,使 Server 與 Client 通信處於異步狀態。當 PHP 作為後端處理耗時任務時,為快速響應前端請求,可採用以下七種方案實現「即發即忘」或異步處理。

方案一:FastCGI 模式下的 fastcgi_finish_request()

立即結束會話返回響應,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.",但日誌文件記錄完整三個時間點。

方案二:fsockopen / cURL 非阻塞 HTTP 請求

fsockopen 實現

$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);

cURL 多線程並發 (curl_multi_*)

$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 完成

方案三:Gearman / Swoole 擴展

  • Gearman:分布式異步任務框架,擁有 PHP 擴展,適合大批量異步任務
  • Swoole:近年熱門擴展,提供豐富異步 API,簡單易用(號稱「重新定義 PHP」,但本質是 C 擴展,與 NodeJS 運行模型不同)

方案四:Redis 隊列 + 後台定時任務

將任務數據寫入 Redis 列表/流,後台 Worker (crontab/Supervisor) 消費處理。大流量架構常見模式。

方案五:系統命令調用後台執行

$cmd = 'nohup php ./processd.php $someVar >/dev/null &';
`$cmd`;  // 反引號執行系統命令

極端情況下可用,但效率較低,不推薦常規使用。

方案六:PHP 協程/生成器

參考國外大神文章:PHP 協程合作式多任務。利用生成器實現用戶態協程調度。

方案七:pcntl_fork 多進程

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。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved