1. 程式人生 > >PHP使用curl multi要注意的問題:每次使用curl multi同時併發多少請求合適

PHP使用curl multi要注意的問題:每次使用curl multi同時併發多少請求合適

PHP的curl multi可以使用多執行緒處理http請求,一定程度上可以提高請求介面的效率。但是,啟用多執行緒也是會消耗資源的事情,那麼每次curl multi同時併發多少個請求合適呢? 接下來做了以下一個實驗,在開始的時候,先說說實驗的結論: 實驗結論 1)首先要檢查發起請求伺服器的網路頻寬是否正常,避免請求伺服器出現頻寬瓶頸。 2)curl multi併發請求併發數有一個閾值,過高的併發不能提升效率,反而會導致請求不成功,這個閾值與服務端的效能有關。 3)CURLOPT_TIMEOUT必須跟進實際業務設定合適的值 實驗程式碼:通過curl multi請求遠端伺服器上的一個介面,介面只是簡單的返回字串'1',驗證請求成功的比例。 <?php $max_request = $argv[1]; $ch_list = array();  $multi_ch = curl_multi_init(); for ($i = 1;$i <= $max_request;++$i) {     $ch_list[$i] = curl_init("http://www.xxx.com/a.php");     curl_setopt($ch_list[$i], CURLOPT_RETURNTRANSFER, true);     curl_setopt($ch_list[$i], CURLOPT_TIMEOUT, 10);     curl_multi_add_handle($multi_ch, $ch_list[$i]);  }  $active = null;  do {     $mrc = curl_multi_exec($multi_ch, $active); //處理在棧中的每一個控制代碼。無論該控制代碼需要讀取或寫入資料都可呼叫此方法。  } while ($mrc == CURLM_CALL_MULTI_PERFORM); //Note: //該函式僅返回關於整個批處理棧相關的錯誤。即使返回 CURLM_OK 時單個傳輸仍可能有問題。  while ($active && $mrc == CURLM_OK) {     if (curl_multi_select($multi_ch) != -1) {//阻塞直到cURL批處理連線中有活動連線。          do {          $mrc = curl_multi_exec($multi_ch, $active);          } while ($mrc == CURLM_CALL_MULTI_PERFORM);      }  } //獲取http返回的結果 $true_request = 0; foreach ($ch_list as $k => $ch) {     $result = curl_multi_getcontent($ch);      curl_multi_remove_handle($multi_ch,$ch);      curl_close($ch);     if ($result == 1) {         $true_request += 1;     } } curl_multi_close($multi_ch); echo $true_request, PHP_EOL; 實驗結果:當併發請求次數大於600之後,成功請求次數並非線性關係,而是在650左右浮動。到目標伺服器檢查nginx日誌,發現請求成功的日誌一共有45925條,而PHP程式返回成功請求的一共有45056條。 此時,猜想,curl請求沒有發出,或者返回值並沒有被成功接收。

我們需要通過curl error錯誤碼,看看發生了什麼事情,這次我們使用併發為800作為一個例子,修改獲取curl返回值的迴圈,打印出curl的錯誤碼: foreach ($ch_list as $k => $ch) {     $result = curl_multi_getcontent($ch);     $errstr = curl_error($ch);     $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);     curl_multi_remove_handle($multi_ch,$ch);     curl_close($ch);     if ($result == 1) {         $true_request += 1;     } else {         echo "{$code}:{$errstr}", PHP_EOL;     } } , 當請求失敗時列印HTTP狀態碼,以及curl的errstr 值,卻發現HTTP狀態碼為0,然而curl的errstr並沒有值。沒有errstr的值,似乎無從入手了,後面使用strace進行嘗試,發現請求失敗的recvfrom返回。 recvfrom(370, 0x3cccc68, 16384, 0, 0, 0) = -1 ECONNRESET (Connection reset by peer) recvfrom(390, "", 16384, 0, NULL, NULL) = 0 ECONNRESET (Connection reset by peer)說明的是服務端關閉了該請求(寫是關閉), recvfrom返回0也是服務端關閉了請求(讀時關閉),看來最大的問題出在server端上!
後記:如何解決Connect time out的問題。 通過資料分析curl耗時,並設定CURLOPT_TIMEOUT的設定在合適的時間內。之前做了一個監控的daemon程式,CURLOPT_TIMEOUT設定為3s,發現有非常多的請求出現Connect time out的情況。CURLOPT_TIMEOUT設定為15s後,Connect time out的情況就少了,那麼CURLOPT_TIMEOUT應該設定為多少比較合適呢?這個可以先收集請求的響應時間,接下來對CURLOPT_TIMEOUT進行優化。 curl_getinfo()函式可以返回幾個有助於我們分析請求時間的指標:
引數名稱 說明
total_time curl所花費的總時間
namelookup_time 域名解析所花費的時間
connect_time 連線目標伺服器所花費的時間
redirect_time 重定向所花費的時間
starttransfer_time 資料傳輸開始時間


PS:指標的單位均是秒(seconds),如果要計算資料傳輸的時間,可以通過total_time - starttransfer_time的差值獲取。 每次請求結束後記錄total_time,通過資料分析,得出合適的CURLOPT_TIMEOUT設定值。
從上圖可以看出,由於伺服器端請求響應時間非常不穩定,但是趨勢是響應時間越大的請求數會越來越小。因此,CURLOPT_TIMEOUT我這面設定為15s是比較合適的,而監控專案上線後,也很小會出現Connect time out 的情況。