PHP 異步 HTTP
在 PHP 代碼中提交異步 HTTP 請求比較經常使用的方式是經過 fsockopen
/fwrite
/fclose
來實現,請參考以下代碼。php
function post($host, $path, $port, $data) { $post = http_build_query($data); $len = strlen($post); $fp = fsockopen($host, $port, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)\n"; return; } $out = "POST $path HTTP/1.1\r\n"; $out .= "Host: $host\r\n"; $out .= "Content-type: application/x-www-form-urlencoded\r\n"; $out .= "Connection: Close\r\n"; $out .= "Content-Length: $len\r\n"; $out .= "\r\n"; $out .= $post . "\r\n"; // echo($out); fwrite($fp, $out); // 註釋掉以下代碼實現不等待 HTTP 響應,從而實現「異步」 // $receive = ''; // while (!feof($fp)) { // $receive .= fgets($fp, 128); // } // echo "<br />" . $receive; fclose($fp); }
這段代碼能夠如期完成異步 HTTP 效果,可是若是提交的服務端有 NGINX 作 CGI 反代的話,可能會致使上游後端 PHP 接收不到該請求。nginx
NGINX 499
查看 NGINX access log,發現這樣的請求會以 499
(Client Closed Request)記錄。肯定問題是由於:客戶端主動端口請求鏈接時,NGINX 不會將該請求代理給上游服務(FastCGI PHP 進程),這個時候 access log 中會以 499 記錄這個請求。數據庫
要解決這個問題須要將 NGINX FastCGI 忽略客戶端中斷配置打開:後端
fastcgi_ignore_client_abort on;
這樣不管客戶端是否斷開,都會將這個請求代理給上游,而且會記錄上游服務處理後的返回狀態。bash
另外,還有一個相似配置 proxy_ignore_client_abort on;
,這個配置是針對其餘類型的反代,PHP 的場景並不適用。服務器
文章來源:https://hacpai.com/article/1444367397136?m=0app
備註:fastcgi_ignore_client_abort on;雖然會忽略客戶端中斷,只是在nginx記錄中不記錄499記錄,並不能從根本上解決499問題。499有多是客戶端關閉鏈接,也有多是請求時間超過upstream_response_time的值,可是即使是upstream_response_time沒有值也會產生499,
upstream_response_time 記錄的是從 Nginx 把請求轉發到後端服務器開始,到後端服務器響應到 Nginx 爲止的時間。異步
upstream_response_time 沒有值有兩種狀況:post
1. 請求沒有被轉發到 upstream serverui
2. Nginx 在等待 upstream response 的時候,客戶端把鏈接關掉了,Nginx 也所以把跟 upstream 之間的鏈接關掉,因此不可能有 upstream response 被返回到 Nginx,天然也不會有 upstream_resposne_time. 在這種狀況下,Nginx 會向客戶端返回 499 。結合數據庫中有記錄來看,客戶端發起一個 http 請求, 請求到 Nginx, Nginx 轉發給後端 PHP,PHP 處理請求往數據庫插記錄,在這時,客戶端發起 abort, Nginx 斷開到 PHP 的鏈接,可是由於 PHP 已經發起了數據庫的操做,這個操做不會被中斷,因此記錄被成功插入到數據庫, Nginx 也向客戶端返回了 499 端口。
就大多數場景而言,若是隻有少許的nginx499是可有可無的,很大一部分只是客戶端主動斷開了請求而已。