在使用curl_multi_*系列函數進行並發請求時,我們通常會開啟多個curl句柄並將它們加入到一個curl_multi句柄中統一管理。
為了確保在調用curl_multi_close釋放資源之前,所有的請求都已經正確完成,我們需要合理地檢測每一個請求的執行狀態。本文將講解一種標準且可靠的方法來實現這一需求。
通常我們會按照以下步驟來處理多個請求:
初始化多個curl單獨句柄。
將這些句柄添加到一個curl_multi句柄中。
使用curl_multi_exec不斷地執行請求。
使用curl_multi_select配合curl_multi_exec檢測是否有請求完成。
逐個移除已完成的請求句柄。
確保所有請求完成後,再調用curl_multi_close 。
下面是一個完整示例,假設我們需要請求多個URL(這里以https://gitbox.net/example1和https://gitbox.net/example2舉例):
<?php
// 初始化 cURL multi 句柄
$multiHandle = curl_multi_init();
// 存放各個 cURL 句柄的數組
$curlHandles = [];
// 要請求的 URL 列表
$urls = [
'https://gitbox.net/example1',
'https://gitbox.net/example2',
];
// 創建並添加每一個 cURL 句柄
foreach ($urls as $i => $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($multiHandle, $ch);
$curlHandles[$i] = $ch;
}
// 執行 multi 請求
$running = null;
do {
$status = curl_multi_exec($multiHandle, $running);
// 防止 CPU 空轉
if ($status == CURLM_OK) {
curl_multi_select($multiHandle);
} else {
break;
}
// 检查是否有已完成的請求
while ($info = curl_multi_info_read($multiHandle)) {
if ($info['msg'] == CURLMSG_DONE) {
// 獲取完成的句柄
$completedHandle = $info['handle'];
// 獲取返回內容
$response = curl_multi_getcontent($completedHandle);
// 可在這里處理返回的數據
echo "請求完成,返回數據長度:" . strlen($response) . PHP_EOL;
// 移除並關閉已完成的句柄
curl_multi_remove_handle($multiHandle, $completedHandle);
curl_close($completedHandle);
}
}
} while ($running > 0);
// 最後關閉 multi 句柄
curl_multi_close($multiHandle);
curl_multi_exec :推進並發請求的執行,返回當前仍在運行的連接數量。
curl_multi_select :用於阻塞等待活動連接,減少CPU 資源浪費。
curl_multi_info_read :檢測是否有請求完成,並可以一次性處理已完成的句柄。
只有當$running == 0 ,並且所有子句柄都已經被移除並關閉後,才調用curl_multi_close 。
如果請求很多,建議添加超時機制,以防某些請求長時間卡住。
curl_multi_select有時返回-1 ,在這種情況下需要使用usleep()進行小幅度等待,防止死循環。
if (curl_multi_select($multiHandle) == -1) {
usleep(100);
}
這樣可以保證腳本更加健壯。
在使用curl_multi_close之前,關鍵是確認所有子請求都已經完成並正確清理,以上的模式是通用且推薦的標準做法。
多了解curl_multi_*系列的行為,有助於寫出更高效、穩定的並發網絡請求代碼!