In PHP, the curl_multi_* series of functions allow you to send multiple HTTP requests concurrently, greatly improving the execution efficiency of the program. However, when many people use curl_multi_close to end multi-request sessions, they tend to ignore an important issue:
If the timeout time is not reasonably controlled, when a request is unresponsive for a long time, the entire application may be stuck, resulting in a very bad user experience. This article will use examples to teach you how to elegantly implement timeout control when using curl_multi_close .
In fact, curl_multi_close is only used to close the curl_multi handle, and it does not control the timeout itself. The real timeout control should occur at the stage of executing the request (i.e. curl_multi_exec and curl_multi_select ).
Simply put, timeout control depends on:
Set timeout on a single curl handle
Record and judge the total time in the execution loop
Here is a practical example. Suppose we have two requests, requesting https://gitbox.net/api/endpoint1 and https://gitbox.net/api/endpoint2 , we hope that all requests will not take more than 10 seconds in total.
<?php
// Create two cURL Handle
$ch1 = curl_init('https://gitbox.net/api/endpoint1');
$ch2 = curl_init('https://gitbox.net/api/endpoint2');
// Set a timeout for each request separately(Optional)
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch1, CURLOPT_TIMEOUT, 8); // The most requests per person8Second
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_TIMEOUT, 8);
// Create a cURL 批处理Handle
$mh = curl_multi_init();
// 添加Handle到批处理Handle中
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
// Perform batch processing
$startTime = microtime(true);
$timeout = 10; // Total timeout(Second)
do {
$status = curl_multi_exec($mh, $active);
// If there is still an active connection
if ($active) {
// Blocking and waiting
$n = curl_multi_select($mh, 1.0);
// if select return -1,Avoid busy waiting
if ($n === -1) {
usleep(100000); // sleep100毫Second
}
}
// Check if timeout
if ((microtime(true) - $startTime) > $timeout) {
echo "The total time to request exceeds{$timeout}Second,Forced interruption。\n";
break;
}
} while ($active && $status == CURLM_OK);
// Read the result
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);
// 关闭Handle
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
curl_close($ch1);
curl_close($ch2);
// Print results
echo "Response 1: " . substr($response1, 0, 100) . "...\n";
echo "Response 2: " . substr($response2, 0, 100) . "...\n";
?>
CURLOPT_TIMEOUT controls the timeout of a single request ;
By recording microtime(true) , the total execution time is manually controlled;
When using curl_multi_select , set an appropriate timeout value (such as 1 second) to avoid excessive CPU usage;
curl_multi_close is only responsible for cleaning resources and does not control any timeout logic .
If you set a too short select timeout (for example, 0.01 seconds), the system load may increase.
If curl_multi_select returns -1, it means there is no file descriptor to wait and requires proper sleep ( usleep ) to avoid busy loops.
When a timeout occurs, it is best to proactively cancel the unfinished request to avoid resource leakage.