In PHP, the curl_multi_* functions provide the ability for asynchronous concurrent requests, typically used in combination with curl_multi_add_handle and curl_multi_exec. When you add multiple cURL handles to a multi-handle using curl_multi_add_handle, curl_multi_remove_handle is used to remove each handle from the multi-handle after the request is completed.
$multiCurl = curl_multi_init();
$curlHandles = [];
<p>// Add multiple cURL handles to the multi-handle<br>
for ($i = 0; $i < 5; $i++) {<br>
$curlHandles[$i] = curl_init("<a rel="noopener" target="_new" class="" href="http://gitbox.net/api/data$i">http://gitbox.net/api/data$i</a>");<br>
curl_multi_add_handle($multiCurl, $curlHandles[$i]);<br>
}</p>
<p>// Execute the requests<br>
do {<br>
$status = curl_multi_exec($multiCurl, $active);<br>
} while ($active);</p>
<p>// Remove each handle<br>
foreach ($curlHandles as $ch) {<br>
curl_multi_remove_handle($multiCurl, $ch);<br>
curl_close($ch);<br>
}</p>
<p>curl_multi_close($multiCurl);<br>
In the code above, we use curl_multi_remove_handle to remove each cURL handle. While this ensures proper resource release, calling curl_multi_remove_handle frequently in a loop can introduce performance overhead, especially when the number of requests is large.
When curl_multi_remove_handle is called, PHP needs to update the internal state of the multi-handle. This is a relatively time-consuming operation, particularly when dealing with a high volume of concurrent requests. After each request completes, PHP must manage and update the state information of the multi-threading system. Frequent removal of handles may lead to the following performance issues:
Frequent Memory Management: When a handle is removed, the underlying memory management system needs to reallocate resources, leading to overhead from memory allocation and deallocation.
Locks and Thread Synchronization: curl_multi_remove_handle involves thread locking during concurrent requests. Frequent calls can result in lock contention, further reducing execution efficiency.
Complexity of the Call Stack: Each call adds depth to the call stack, and with high concurrency, the increasing call stack may impact overall performance.
Therefore, optimizing these operations is essential for improving performance, especially when performing frequent concurrent requests.
Instead of calling curl_multi_remove_handle immediately after each request is completed, we can remove all handles after all requests have finished. This approach avoids frequent resource updates and reduces performance overhead.
$multiCurl = curl_multi_init();
$curlHandles = [];
<p>// Add multiple cURL handles<br>
for ($i = 0; $i < 5; $i++) {<br>
$curlHandles[$i] = curl_init("<a rel="noopener" target="_new" class="" href="http://gitbox.net/api/data$i">http://gitbox.net/api/data$i</a>");<br>
curl_multi_add_handle($multiCurl, $curlHandles[$i]);<br>
}</p>
<p>// Execute concurrent requests<br>
do {<br>
$status = curl_multi_exec($multiCurl, $active);<br>
} while ($active);</p>
<p>// Remove all handles at once<br>
foreach ($curlHandles as $ch) {<br>
curl_multi_remove_handle($multiCurl, $ch);<br>
curl_close($ch);<br>
}</p>
<p>curl_multi_close($multiCurl);<br>
By removing all handles after all requests have finished, we reduce unnecessary intermediate operations, which improves performance.
In some cases, we can reduce the frequency of curl_multi_remove_handle calls by adjusting the number of concurrent requests. For example, we can batch process requests and remove handles only after each batch is complete.
$multiCurl = curl_multi_init();
$curlHandles = [];
<p>// Assume processing 10 requests at a time<br>
$batchSize = 10;</p>
<p>for ($i = 0; $i < 100; $i++) {<br>
$curlHandles[$i] = curl_init("<a rel="noopener" target="_new" class="" href="http://gitbox.net/api/data$i">http://gitbox.net/api/data$i</a>");<br>
curl_multi_add_handle($multiCurl, $curlHandles[$i]);</p>
<pre class="overflow-visible!"><div class="contain-inline-size rounded-2xl border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary"><div class="flex items-center text-token-text-secondary px-4 py-2 text-xs font-sans justify-between h-9 bg-token-sidebar-surface-primary dark:bg-token-main-surface-secondary select-none rounded-t-2xl">php</div><div class="sticky top-9"><div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"><div class="bg-token-sidebar-surface-primary text-token-text-secondary dark:bg-token-main-surface-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"><button class="flex gap-1 items-center select-none py-1" aria-label="复制"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-xs"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制</button><button class="flex items-center gap-1 py-1 select-none"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-xs"><path d="M2.5 5.5C4.3 5.2 5.2 4 5.5 2.5C5.8 4 6.7 5.2 8.5 5.5C6.7 5.8 5.8 7 5.5 8.5C5.2 7 4.3 5.8 2.5 5.5Z" fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M5.66282 16.5231L5.18413 19.3952C5.12203 19.7678 5.09098 19.9541 5.14876 20.0888C5.19933 20.2067 5.29328 20.3007 5.41118 20.3512C5.54589 20.409 5.73218 20.378 6.10476 20.3159L8.97693 19.8372C9.72813 19.712 10.1037 19.6494 10.4542 19.521C10.7652 19.407 11.0608 19.2549 11.3343 19.068C11.6425 18.8575 11.9118 18.5882 12.4503 18.0497L20 10.5C21.3807 9.11929 21.3807 6.88071 20 5.5C18.6193 4.11929 16.3807 4.11929 15 5.5L7.45026 13.0497C6.91175 13.5882 6.6425 13.8575 6.43197 14.1657C6.24513 14.4392 6.09299 14.7348 5.97903 15.0458C5.85062 15.3963 5.78802 15.7719 5.66282 16.5231Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M14.5 7L18.5 11" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>编辑</button></div></div></div><div class="overflow-y-auto p-4" dir="ltr">if (count($curlHandles) == $batchSize) {
// Execute concurrent requests
do {
$status = curl_multi_exec($multiCurl, $active);
} while ($active);
// Remove all handles of the current batch
foreach ($curlHandles as $ch) {
curl_multi_remove_handle($multiCurl, $ch);
curl_close($ch);
}
// Clear the handle array for the current batch
$curlHandles = [];
}
}
// If there are remaining requests, continue processing
if (count($curlHandles) > 0) {
do {
$status = curl_multi_exec($multiCurl, $active);
} while ($active);
// Remove remaining handles
foreach ($curlHandles as $ch) {
curl_multi_remove_handle($multiCurl, $ch);
curl_close($ch);
}
}
curl_multi_close($multiCurl);
By batching requests, we can significantly reduce the number of curl_multi_remove_handle calls, thereby improving performance.
In some cases, if you frequently make the same API calls, you might consider enabling HTTP persistent connections (Keep-Alive). This allows cURL to reuse the same connection for multiple requests, reducing the overhead of establishing new connections and minimizing the frequency of curl_multi_remove_handle calls.
You can disable connection reuse by setting CURLOPT_FORBID_REUSE to true to ensure a new connection is used for each request. However, keeping the connection reused generally improves performance unless there is a specific need to avoid it.
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Connection: keep-alive',
]);
By using persistent connections, the frequency of connection creation and destruction is reduced, which improves overall performance.
If your request volume is very high, PHP's cURL may no longer be the most optimal choice. In such cases, consider using more advanced concurrency methods, such as using Guzzle or other modern HTTP clients, which generally offer better performance and memory management when handling high-concurrency requests.