今天跑腳本遇到一個奇怪的問題,就是cURL請求到後期會出現程序阻塞卡死,無異常無響應,一直掛起,腳本也不會自動結束。跟對方溝通後說,「哥們兒,是否是大家的程序有問題啊,這邊研發排查了,說12點30左右沒有收到大家的請求。而後我本身用網上的json工具請求了,一下就經過了」。是否是很尷尬,關鍵是根本不知道爲何。而後就是不停的嘗試重跑腳本,偶爾有些腳本就跑過了,但好景不長,隨時均可能出現無效的時候。一直看,請求一開始都是能正常完成的,越日後執行時間也變得逐漸增加,而後就可能變成死鏈接。就想到,確定是cURL發送的請求有的無響應失效了,已經成殭屍鏈接,還一直阻塞腳本,致使資源一直存在,可是並不能繼續使用。php
找了各類資料,還想嘗試寫一個相似定時器的定西,若是腳本執行超過1分鐘,則強制丟棄掉該cURL連接。不知道PHP怎麼實現這樣的需求,還猶豫要不要用go來實現。涉及到json,xml等,用go畢竟沒有PHP方便,就去手冊閱讀cURL的內容,企圖找到一點有用的東西。並且cURL命令行工具是有重試功能的,猜測擴展確定也有。可是該如何配置呢?json
看了PHP超時處理全面總結這篇文章中的超時,才知道由於個人代碼設置cURL鏈接選項時只設置了鏈接超時時間,並無設置執行超時時間。觀察一般10秒內正常都應該返回數據,我就設置了超時和執行時間都是30秒。增長這個參數限制後,總算能捕捉到無響應的請求了,剩下的就是如何處理在規定時間內沒法返回結果的資源了。segmentfault
就是這幾個參數沒有了解過,給排查問題浪費了整整一個下午。curl
/** * CURLOPT_TIMEOUT設置cURL容許執行的最長秒數。 * CURLOPT_TIMEOUT_MS設置cURL容許執行的最長毫秒數。(在cURL7.16.2中被加入。從PHP5.2.3起可以使用。) * CURLOPT_CONNECTTIMEOUT在發起鏈接前等待的時間,若是設置爲0,則無限等待。 * CURLOPT_CONNECTTIMEOUT_MS嘗試鏈接等待的時間,以毫秒爲單位。若是設置爲0,則無限等待。在cURL7.16.2中被加入。從PHP5.2.3開始可用。 * CURLOPT_DNS_CACHE_TIMEOUT設置在內存中保存DNS信息的時間,默認爲120秒。 */
增長執行超時後的請求設置函數。函數
/** * curl請求 * * @param $url * @param string $postData * @param int $timeout * @return array|mixed * @throws Exception */ protected static function post($url, $postData = '', $timeout = 5) { $ret = array(); $times = 5; do { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HEADER, false); if ($postData != '') { curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // 重要, 該處不能丟 curl 執行最大秒數 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); $output = curl_exec($ch); if ($errNo = curl_errno($ch)) { error_log("Error [$errNo]: " . curl_error($ch)); } else { $ret = json_decode($output, true); // 解析的結果集爲空時中止查詢 if (!is_array($ret) && !trim($ret)) { throw new Exception(__METHOD__ . ": cURL調用失敗, 信息爲: " . $output); } unset($output); } curl_close($ch); if (isset($ret[0]) && $ret[0]) { return $ret; } } while ($times--); exit(__METHOD__ . ": cURL請求重試至 {$times} 次後仍無響應, 執行退出"); }
超時時的提示信息設置如:工具
error_log("Error [$errNo]: " . curl_error($ch));
我設置的鏈接時間和執行時間限制都是30秒。post
Error [28]: Operation timed out after 30000 milliseconds with 0 bytes received
能夠輸出查看,針對這種超時,直接丟棄該次鏈接,從新初始化一次資源請求便可。固然這裏嘗試重試6次,6次都還沒法正常執行完畢,就只能在想別的辦法了。雖然最終並不知道爲何會有鏈接失效,可是這樣以後,就能保證基本能夠完成任務了。url
我執行一個月的跑數腳本,全部超時狀況就有這麼多,幸運的是最終都沒能等到重試6次,就請求成功了。從錯誤類型中,看到確實是有一些請求在30秒內未能執行完畢。spa
Error [28]: Operation timed out after 30000 milliseconds with 0 bytes received Error [28]: Operation timed out after 30000 milliseconds with 0 bytes received Error [28]: Operation timed out after 30000 milliseconds with 62399 out of 323196 bytes received