在使用php curl進行網絡請求時,我們有時會遇到curl阻塞的問題。即:程序執行開始調用curl相關函數后,一直處于等待狀態,直到curl函數執行完成或者超時錯誤拋出。
一個常見的例子就是我們使用curl獲取第三方API的數據時,請求數據的過程可能需要一些時間,而php程序默認為同步阻塞方式,必須等待curl請求完成才能繼續執行下面的邏輯。如果請求數據的過程很慢,就會導致用戶體驗不佳。
不過,我們可以通過一些方法來解決php curl阻塞的問題,下面為大家詳細介紹:
使用curl_multi_exec函數實現非阻塞請求
// 假設我們要請求兩個URL $ch1 = curl_init(); curl_setopt($ch1, CURLOPT_URL, "http://www.example.com/api1"); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); $ch2 = curl_init(); curl_setopt($ch2, CURLOPT_URL, "http://www.example.com/api2"); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); // 將兩個請求加入同一curl批處理會話 $mh = curl_multi_init(); curl_multi_add_handle($mh, $ch1); curl_multi_add_handle($mh, $ch2); // 執行curl批處理請求 $running = null; do { curl_multi_exec($mh, $running); } while ($running >0); // 處理請求結果 $response1 = curl_multi_getcontent($ch1); $response2 = curl_multi_getcontent($ch2); echo $response1; echo "
"; echo $response2;
上述代碼通過使用curl_multi_exec函數實現了非阻塞請求。我們將需要請求的URL加入同一curl批處理會話中,再通過curl_multi_exec函數不斷執行該批處理請求,直到所有請求執行完畢才停止執行。
值得注意的是,在使用curl_multi_exec函數時,必須在循環內調用該函數,以確保每個請求都能得到執行。例如上述代碼中的do-while循環。
使用多線程方式實現非阻塞請求
// 創建一個php線程對象 $thread = new Thread(); // 設置線程參數 $thread->setStackSize(32000000); $thread->setStopOnError(true); // 啟動線程,并將curl請求傳入 $thread->start(function($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($ch); curl_close($ch); return $result; }, "http://www.example.com/api"); // 獲取線程返回結果 while (!$thread->isTerminated()) { usleep(10000); } $response = $thread->getThreadValue(); echo $response;
上述代碼通過使用Thread擴展庫,實現了多線程方式的非阻塞請求。我們將需要請求的URL傳入線程對象,并在啟動線程時傳入curl請求函數,以便在線程內執行。
Thread是PHP官方擴展庫,使用起來很方便,但是要實現線程安全,要求代碼的風格和處理方式有所改變。
使用隊列方式實現非阻塞請求
// 創建一個隊列對象 $queue = new Queue(); // 將需要請求的URL加入隊列 $queue->enqueue("http://www.example.com/api1"); $queue->enqueue("http://www.example.com/api2"); $queue->enqueue("http://www.example.com/api3"); // 定義處理函數,將curl請求放入隊列,并存儲結果 function processQueue($queue) { $results = []; while(!$queue->isEmpty()) { $url = $queue->dequeue(); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($ch); curl_close($ch); $results[] = $result; } return $results; } // 啟動隊列處理 $result = $queue->run("processQueue"); echo $result[0]; echo "
"; echo $result[1]; echo "
"; echo $result[2];
上述代碼通過使用隊列方式實現了非阻塞請求。我們將需要請求的URL加入隊列中,再定義一個處理函數,用于依次處理隊列中的URL,并記錄每個請求的返回結果。通過啟動隊列處理,讓該函數一直執行,以確保隊列中的URL全部被處理。
需要注意的是,在使用隊列方式時,我們需要定義處理函數,以便隊列處理器能夠調用該函數,并將隊列傳遞給該函數。而在隊列處理完成后,我們需要通過返回值的方式,將每個請求的結果記錄下來。
總結
php curl阻塞是一種常見的問題,在處理大量數據、請求第三方API等場景下尤為明顯,但是通過使用前述方法,我們可以有效地將其解決,提高程序的執行效率。如果你使用的是php框架,也可以考慮使用該框架的非阻塞請求方式,來進一步提升性能。
本文介紹的三種方法都有其優劣,可以根據具體情況選擇使用。不過,無論使用哪種方式,我們都需要充分考慮并發、錯誤處理等因素,以確保程序健壯性和可維護性。