private function fsock_asy_do($get){ $fp = fsockopen("ssl://www.xxx.com", 443, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\n"; } else { stream_set_blocking($fp,0);//開啟非阻塞模式 $out = "GET /".$get." HTTP/1.1\r\n"; $out .= "Host: www.xxxx.com\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); /*忽略執行結果 while (!feof($fp)) { echo fgets($fp, 128); }*/ usleep(1000); fclose($fp); } }
在這段代碼里面,調用后忽略執行結果直接返回,可用於php異步執行。
在nginx服務器上有一個比較詭異的情況就是有時候無法調用異步的腳本。
查閱相關資料后,是nginx 499 的問題。
其中解決方案有以下,經過對每一個方案的驗證最終得出結果:
1、NGINX 499
查看 NGINX access log,發現這樣的請求會以 499
(Client Closed Request)記錄。確定問題是因為:客戶端主動端口請求連接時,NGINX 不會將該請求代理給上游服務(FastCGI PHP 進程),這個時候 access log 中會以 499 記錄這個請求。
要解決這個問題需要將 NGINX FastCGI 忽略客戶端中斷配置打開:
fastcgi_ignore_client_abort on;
這樣無論客戶端是否斷開,都會將這個請求代理給上游,並且會記錄上游服務處理后的返回狀態。
2、NGINX 線程原因
將nginx的worker_processes 由之前的auto修改為2(我的是單核服務器)
3、NGINX 499
nginx對499的定義是”client has closed connection”,並且在這些情況下會返回這個狀態碼:
- upstream 在收到讀寫事件處理之前時發現連接不可用。
- server處理請求未結束,而client提前關閉了連接。
- upstream出錯,執行next_upstream時發現連接不可用。
一個不安全的做法是在fclose之前,讓當前的進程先睡眠一段時間;我這里設置為10毫秒,這10毫秒的延遲對我完成整個請求的影響不大,同時我也認為nginx一定能在10毫米內把請求轉到fastcgi去執行。這個時間間隔很難把握,不能保證php一定有執行到。
這種方式並不是真正的異步,只是很取巧的強制關閉連接而不等待服務器端響應。所以在Laruence的那2篇文章中,有2個問題:
- PHP使用fsock不能叫做異步,只是偽異步。
- fwrite之后馬上執行fclose,nginx會直接返回499
由於我的代碼上面usleep為1000,初步估計是時間不夠導致沒發出去就close了,所以調整為20000。並進行最后測試。
結果晚點更新。
更新結果:測試2天結果顯示正常,的確是usleep數值過小的問題。