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

PHP 非阻塞與異步處理七大方案深度解析:從 fastcgi_finish_request 到 Swoole

     編輯:PHP 綜合知識

前言

PHP 傳統同步阻塞模型在處理耗時任務(郵件發送、圖片處理、第三方 API 呼叫)時,會嚴重拖慢回應速度。本文系統梳理 PHP 非阻塞/異步處理的七大主流方案,涵蓋 FPM 特性、Socket/CURL 非阻塞、擴充框架、佇列系統、進程分叉等,並附上程式碼範例與選型建議。

方案一:fastcgi_finish_request() —— FPM 專屬「早回應」

適用場景:HTTP 請求處理完邏輯,需繼續執行耗時任務(日誌、統計、緩存預熱)。

<?php
echo "程式開始";
file_put_contents('log.txt', 'start-time:' . date('Y-m-d H:i:s'), FILE_APPEND);

// 關鍵:立即結束 HTTP 回應,瀏覽器收到內容,但 PHP 進程繼續執行
fastcgi_finish_request();

sleep(1);
echo 'debug...'; // 瀏覽器不會收到
file_put_contents('log.txt', '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);
?>

優點:零依賴、極簡、FPM 原生支援
缺點:僅限 FPM、任務仍佔用 FPM 工作進程、高併發易耗盡進程池

方案二:fsockopen / cURL 非阻塞 —— 輕量級異步 HTTP

1. fsockopen 手工構建 HTTP 請求

<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) die('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); // 立即關閉,不等待回應
?>

2. cURL 多執行緒並發 curl_multi_*

<?php
$cmh = curl_multi_init();
$ch1 = curl_init();
curl_setopt($ch1, CURLOPT_URL, "https://localhost:6666/child.php");
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);
curl_multi_add_handle($cmh, $ch1);

// 非阻塞執行
$active = null;
do {
    curl_multi_exec($cmh, $active);
} while ($active);

echo "主程式結束\n";
// 此時 child.php 可能仍在執行
?>

優點:無額外擴充、支援並發多請求
缺點:錯誤處理複雜、無重試機制、難以獲取回應結果

方案三:Gearman / Swoole —— 專業異步框架

Gearman

  • 分散式任務佇列,支援多語言 Worker
  • PHP 擴充 gearman,適合大批量異步任務
  • 架構:Client → Job Server → Worker

Swoole ⭐ 推薦

  • 重新定義 PHP:內建協程、非阻塞 IO、WebSocket、HTTP Server
  • 單進程支援萬級併發,效能逼近 Node.js/Go
  • 常用 API:Swoole\Timer、Swoole\Coroutine、Swoole\Http\Client
// Swoole 協程異步 HTTP
go(function () {
    $client = new Swoole\Coroutine\Http\Client('api.example.com', 80);
    $client->get('/async-task');
    var_dump($client->body);
});

方案四:Redis 佇列 + 排程任務 —— 架構級解耦

流程:Web 請求 → 寫入 Redis List/Stream → 排程 Worker 消費

// 生產者(Web 端)
$redis->lPush('async_queue', json_encode(['type' => 'email', 'to' => 'user@example.com']));

// 消費者(crontab 每分鐘執行)
while ($job = $redis->rPop('async_queue')) {
    $data = json_decode($job, true);
    // 處理任務...
}

優點:解耦、可擴展、支援優先級、重試、死信佇列
生產建議:使用 Laravel Horizon、PHP-Redis-Queue 等成熟庫

方案五:系統命令背景執行 —— 簡暴力但有效

<?php
$cmd = 'nohup php ./processd.php ' . escapeshellarg($someVar) ' >/dev/null 2>&1 &';
`$cmd`; // 或 exec($cmd), shell_exec($cmd)
?>
  • nohup:忽略掛起信號
  • &:背景執行
  • /dev/null:丟棄輸出

缺點:進程管理困難、無狀態追蹤、安全風險(需 escapeshellarg)、Windows 不支援

方案六:PHP 協程 —— 使用者態輕量執行緒

參考:nikic 協程實作

  • 基於 yield 實作協作式多任務
  • PHP 5.5+ 生成器語法支援
  • 現代框架(Laravel Octane、Swoole)皆建立於此

方案七:pcntl_fork 多進程 —— 經典 Unix 方式

<?php
if (($pid = pcntl_fork()) == 0) {
    // 子進程
    child_func();
    exit(0);
} else {
    // 父進程
    father_func();
}

function father_func() {
    echo "父進程 PID: " . getmypid() . "\n";
    // 避免殭屍進程:收割子進程
    pcntl_wait($status);
}

function child_func() {
    sleep(6);
    echo "子進程結束 PID: " . getmypid() . "\n";
}
?>

關鍵:父進程必須 pcntl_wait() 或 pcntl_signal(SIGCHLD, SIG_IGN) 防殭屍進程
限制:僅 CLI 模式、Windows 不支援、記憶體複製開銷大

八、方案選型決策表

場景推薦方案理由
簡單日誌/統計,無回應需求fastcgi_finish_request零成本、FPM 原生
呼叫外部 API,需並發、重試Swoole / cURL Multi高效能、錯誤處理完善
核心業務異步化,需可靠性、監控Redis/RabbitMQ + Worker架構解耦、生產級
CPU 密集運算(圖片/影片處理)pcntl_fork / Swoole Task多核並行、隔離記憶體
長連接/即時推送Swoole / Workerman原生 WebSocket、協程支援

九、結語

PHP 非阻塞演進路徑:fastcgi_finish_request → Socket/CURL 非阻塞 → 進程分叉 → 佇列解耦 → 協程/ Swoole 原生異步。現代專案首選 Swoole + 佇列 架構,既享受協程高併發,又具備佇列可靠性。舊專案漸進式遷移可先引入 fastcgi_finish_request 或 Redis 佇列,降低風險。

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