php執行一段程序,有可能幾毫秒就執行完畢,也有可能耗時較長。
例如,使用者下單這個事件,如果呼叫了些第三方服務進行發送郵件、簡訊、推播等通知,可能導致前端一直在等待。
而有的時候,我們並不在乎這些耗時腳本的回傳結果,只要執行就行了。這時候就需要採用非同步的方式執行。
眾所周知,PHP沒有直接支援多執行緒這種東西。我們可以採用折衷的方式實現。這裡主要說的是fsockopen
。
透過fsockopen發送請求並忽略回傳結果,程式可以馬上回傳。
範例程式碼:
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\n"; } else { $out = "GET /backend.php HTTP/1.1\r\n"; $out .= "Host: www.example.com\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); /*忽略执行结果 while (!feof($fp)) { echo fgets($fp, 128); }*/ fclose($fp); }
要注意的是我們需要手動拼出header頭資訊。透過開啟註解部分,可以查看請求傳回結果,但那時又變成同步的了,因為程式會等待回傳結果才結束。
實際測試的時候發現,不忽略執行結果,調試的時候每次都會成功發送sock請求;但忽略執行結果,經常看到沒有成功發送sock請求。查看nginx日誌,發現很多狀態碼為499的請求。
後來找到了原因:
fwrite
之後馬上執行fclose
,nginx會直接回傳499,不會把請求轉發給php處理。
客戶端主動埠請求連線時,NGINX 不會將該請求代理給上游服務(FastCGI PHP 進程),這個時候 access log 中會以 499 記錄這個請求。
解決方案:
1)nginx.conf增加設定
# 忽略客户端中断 fastcgi_ignore_client_abort on;
2)fwrite之後使用usleep函數休眠20毫秒:
usleep(20000);
後來測試就沒有發現失敗的情況了。
附上完整程式碼:
<?php /** * 工具类 * */ class FsockService { public static function post($url, $param){ $host = parse_url($url, PHP_URL_HOST); $port = 80; $errno = ''; $errstr = ''; $timeout = 30; $data = http_build_query($param); // create connect $fp = fsockopen($host, $port, $errno, $errstr, $timeout); if(!$fp){ return false; } // send request $out = "POST ${url} HTTP/1.1\r\n"; $out .= "Host:${host}\r\n"; $out .= "Content-type:application/x-www-form-urlencoded\r\n"; $out .= "Content-length:".strlen($data)."\r\n"; $out .= "Connection:close\r\n\r\n"; $out .= "${data}"; fwrite($fp, $out); //忽略执行结果;否则等待返回结果 // if(APP_DEBUG === true){ if(false){ $ret = ''; while (!feof($fp)) { $ret .= fgets($fp, 128); } } usleep(20000); //fwrite之后马上执行fclose,nginx会直接返回499 fclose($fp); } public static function get($url, $param){ $host = parse_url($url, PHP_URL_HOST); $port = 80; $errno = ''; $errstr = ''; $timeout = 30; $url = $url.'?'.http_build_query($param); // create connect $fp = fsockopen($host, $port, $errno, $errstr, $timeout); if(!$fp){ return false; } // send request $out = "GET ${url} HTTP/1.1\r\n"; $out .= "Host:${host}\r\n"; $out .= "Connection:close\r\n\r\n"; fwrite($fp, $out); //忽略执行结果;否则等待返回结果 // if(APP_DEBUG === true){ if(false){ $ret = ''; while (!feof($fp)) { $ret .= fgets($fp, 128); } } usleep(20000); //fwrite之后马上执行fclose,nginx会直接返回499 fclose($fp); } } ?>
更多相關php知識,請造訪php教學!
以上是php中使用fsockopen實作非同步請求(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!