PHP并發請求優化:使用curl_multi_select()
實現高效的多請求處理
背景
最近在項目中遇到一個需求,需要從多個 1 級網站(超過 200 個)獲取數據,并且是通過 POST 請求瞬間發送到這些網站上。開始時我直接使用了 curl_exec()
逐一發送請求,但是很快就發現了一個問題:這個方法是阻塞式的,只能一次發送一個請求。處理 200 多個請求時,性能非常差。顯然,這種方法不適合大規模的并發請求。
為了解決這個問題,我查閱了大量資料,最后找到了一個不錯的方案——使用 curl_multi_exec()
和 curl_multi_select()
實現并發請求。這樣不僅能大幅提升性能,還能避免 CPU 資源的浪費。下面是我的經驗分享,希望對大家有幫助。
1. 為什么不用 curl_exec()
?
最開始,我像往常一樣使用 curl_exec()
來發送請求,但是遇到了以下兩個問題:
- 性能低下:每次只能處理一個請求,多個請求只能串行執行,耗時較長。
- 資源消耗大:頻繁的網絡請求導致 CPU 長時間空轉,系統資源被大量占用。
顯然,這樣的方式不適合處理 200 多個并發請求,尤其是當項目需要高效、實時獲取數據的時候。所以我需要找到一種更好的方式來實現并發請求。
2. 使用 curl_multi_exec()
實現并發請求
為了提升并發請求的效率,我決定使用 curl_multi_exec()
。它可以將多個 cURL 請求組合在一起并行執行,從而大大提高了處理效率。
基本使用方法
首先,簡單介紹一下如何使用 curl_multi_exec()
。這個函數允許同時執行多個 cURL 請求,而不需要等待每個請求的完成。
$multiCurl = curl_multi_init(); // 初始化multi cURL句柄// 創建單個cURL請求
$ch1 = curl_init('https://example.com');
$ch2 = curl_init('https://example2.com');// 將單個cURL句柄加入multi句柄中
curl_multi_add_handle($multiCurl, $ch1);
curl_multi_add_handle($multiCurl, $ch2);// 執行并發請求
do {$status = curl_multi_exec($multiCurl, $active);
} while ($active && $status == CURLM_OK);// 關閉cURL句柄
curl_multi_remove_handle($multiCurl, $ch1);
curl_multi_remove_handle($multiCurl, $ch2);
curl_multi_close($multiCurl);
通過這種方法,我可以同時發送多個請求,而不必等待每個請求逐一完成。對比 curl_exec()
,處理多個請求的效率提高了很多。
3. 優化:結合 curl_multi_select()
降低系統資源消耗
雖然 curl_multi_exec()
可以并發處理多個請求,但它本身并不會阻塞,因此我發現頻繁調用它會導致系統的 CPU 資源大量消耗。這個問題在處理大量請求時尤其明顯。
后來經過一番摸索,發現了 curl_multi_select()
。這個函數可以阻塞進程,直到有請求完成或發生變化(如接收到數據等)。這樣可以減少不必要的 CPU 占用,讓系統只在有必要時才執行檢查。
curl_multi_select()
的用法
通過 curl_multi_select()
,我能有效減少 CPU 的空轉等待。它的工作方式很簡單:在等待時段內,如果有請求狀態發生變化(比如完成、錯誤等),它就會繼續執行。如果沒有變化,進程會阻塞直到超時。
$multiCurl = curl_multi_init();// 創建多個cURL請求
$ch1 = curl_init('https://example.com');
$ch2 = curl_init('https://example2.com');// 添加請求句柄
curl_multi_add_handle($multiCurl, $ch1);
curl_multi_add_handle($multiCurl, $ch2);do {$status = curl_multi_exec($multiCurl, $active);if ($active) {// 使用 curl_multi_select 等待請求狀態的變化,避免頻繁CPU循環curl_multi_select($multiCurl);}} while ($active && $status == CURLM_OK);// 獲取并處理每個請求的響應
curl_multi_remove_handle($multiCurl, $ch1);
curl_multi_remove_handle($multiCurl, $ch2);
curl_multi_close($multiCurl);
在這段代碼中,curl_multi_select()
用于等待活動發生,只有當請求完成或者有數據時,才會繼續執行,節省了 CPU 資源。
4. 實際應用:批量并發請求
為了處理項目中的大規模并發請求,我最終實現了一個可以同時發送多個請求的批處理腳本。以下是完整代碼:
<?php// 初始化multi cURL句柄
$multiCurl = curl_multi_init();
$curlHandles = [];// 準備多個URL
$urls = ['https://example.com','https://example2.com','https://example3.com','https://example4.com'
];// 創建cURL句柄并將其添加到multi句柄中
foreach ($urls as $i => $url) {$ch = curl_init($url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回結果而不是直接輸出$curlHandles[$i] = $ch;curl_multi_add_handle($multiCurl, $ch);
}do {$status = curl_multi_exec($multiCurl, $active);if ($active) {// 阻塞等待直到有請求完成curl_multi_select($multiCurl);}} while ($active && $status == CURLM_OK);// 獲取并處理每個請求的響應
foreach ($curlHandles as $i => $ch) {$response = curl_multi_getcontent($ch);echo "Response from URL {$urls[$i]}: $response\n";curl_multi_remove_handle($multiCurl, $ch); // 移除句柄
}// 關閉multi句柄
curl_multi_close($multiCurl);
通過這個方法,我成功實現了向 200 多個 1 級網站同時發起 POST 請求的需求,速度比最初的逐一請求快了很多倍。而且系統資源消耗也得到了很好的控制,避免了 CPU 的過度占用。
5. 總結
通過使用 curl_multi_exec()
和 curl_multi_select()
,我們可以大幅提升 PHP 中并發請求的處理效率,特別是當需要處理大量外部請求時。這種方法不僅能提高響應速度,還能有效降低系統資源的消耗,是非常適合大規模并發處理的解決方案。
希望這篇文章能為大家提供一些幫助,如果有類似需求的開發者也可以嘗試這個方法。有什么問題或建議,歡迎留言交流!