Web 請求中若直接執行耗時腳本(如發送大量郵件、影片轉碼),使用者需等待回應,體驗極差。利用 fsockopen() 發起 HTTP 請求後不等待回應即關閉連線,即可達到「觸發即返回」的非同步效果。
function triggerRequest($url, $post_data = [], $cookie = []) {
$url_parts = parse_url($url);
$host = $url_parts['host'];
$port = $url_parts['port'] ?? 80;
$path = $url_parts['path'] . ($url_parts['query'] ? '?' . $url_parts['query'] : '');
$fp = fsockopen($host, $port, $errno, $errstr, 30);
if (!$fp) return false;
$method = empty($post_data) ? 'GET' : 'POST';
$header = "$method $path HTTP/1.1\r\n";
$header .= "Host: $host\r\n";
$header .= "Connection: Close\r\n";
if (!empty($cookie)) {
$cookie_str = 'Cookie: ' . base64_encode(http_build_query($cookie, '', '; ')) . "\r\n";
$header .= $cookie_str;
}
if (!empty($post_data)) {
$body = http_build_query($post_data);
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($body) . "\r\n\r\n";
$header .= $body;
} else {
$header .= "\r\n";
}
fwrite($fp, $header);
fclose($fp); // 立即關閉,不讀取回應
return true;
}
<?php
// 1. 忽略用戶端斷線,讓腳本繼續跑完
ignore_user_abort(true);
// 2. 移除執行時間限制(預設 30 秒)
set_time_limit(0);
// 你的長時間任務邏輯...
// 例如:發送郵件、生成報表、視頻轉碼等
| 常數 | 值 | 說明 |
|---|---|---|
| NORMAL | 0 | 正常執行中 |
| ABORTED | 1 | 用戶端中斷連線(如點擊停止、關閉瀏覽器) |
| TIMEOUT | 2 | 執行時間超過 max_execution_time |
可用 connection_status() 取得當前狀態(位元組合,如 3 = ABORTED + TIMEOUT)。
register_shutdown_function(function() {
$status = connection_status();
if ($status & CONNECTION_ABORTED) {
// 用戶端斷線,記錄日誌或清理資源
}
if ($status & CONNECTION_TIMEOUT) {
// 執行超時
}
});
Redis、RabbitMQ、Beanstalkd 等,適合高併發、需重試、監控的生產系統。使用 fsockopen() 搭配 ignore_user_abort(true) 和 set_time_limit(0) 是 PHP 傳統環境下實現「即觸即走」非同步任務的最輕量方案。對於關鍵業務,建議升級至佇列系統保證可靠性。