Varför hämta filer parallellt?
I normala fall när du surfar runt på webben så hanterar webbläsaren alla nerladdningar. Alla moderna webbläsare hämtar de filer (CSS, JavaScript, osv) som finns på en webbplats parallellt. Dvs, alla filer hämtas samtidigt istället för att nerladdningarna ska behöva vänta på varandra innan de kan börja.
Låt säga att du hämtar information från andra webbplatser för att lagra och sedan presentera den på din egna webbplats. Ett exempel kan vara affiliatenätverk, som ofta erbjuder information om produkter via XML-filer. Här kan det vara trevligt att hämta all data parallellt för att snabba upp processen.
Ett annat exempel kan vara en robot som vill hämta tusentals dokument för att sedan indexera. Om detta inte skulle ske parallellt så skulle detta ta enormt mycket längre tid.
PHP och libcurl
PHP (och många andra språk) har stöd för biblioteket libcurl. Med libcurl är det väldigt enkelt att hämta filer genom olika protokoll samtidigt som det är riktigt kraftfullt och flexibelt eftersom du kan konfigurera det på så många olika sätt.
Normalt när du hämtar en fil med libcurl använder du följande:
<?php
// initiate libcurl.
if ($ch = curl_init('http://www.ifconfig.se/'))
{
// configure libcurl.
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 32);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// execute libcurl.
$body = curl_exec($ch);
// close nicely.
curl_close($ch);
}
?>
Detta är ganska så mycket kod för något som file_get_contents() kan göra på en rad. Dock kräver file_get_contents() att allow_url_fopen är aktiverat i php.ini, vilket enligt mig är en säkerhetsrisk. Dessutom får du betydligt mer kontroll med libcurl och du kan även konfigurera libcurl att hämta komprimerade filer, vilket i sig spar bandbredd.
Att hämta filer parallellt med libcurl
För att hämta filer parallellt måste man först initiera ett objekt, som vi sedan kan lägga till vanliga curl objekt till. När vi lagt till alla objekt så använder vi oss av curl_multi_exec() för att hämta alla filerna samtidigt.
<?php
// urls to fetch.
$urls = array(
'http://www.ifconfig.se/',
'http://www.facebook.com/',
'http://www.twitter.com/',
'http://www.youtube.com/',
'http://www.google.com/'
);
$total = count($urls);
// initiate the multi-handler.
$cm = curl_multi_init();
// loop through all urls and initiate all objects.
for($i=0;$i<$total;$i++)
{
// initiate a libcurl object for each url.
$ch[$i] = curl_init($urls[$i]);
// configure each object.
curl_setopt($ch[$i], CURLOPT_AUTOREFERER, 1);
curl_setopt($ch[$i], CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch[$i], CURLOPT_MAXREDIRS, 32);
curl_setopt($ch[$i], CURLOPT_HEADER, 0);
curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);
// add this object to the multi-handler.
curl_multi_add_handle($cm, $ch[$i]);
}
// execute the multi-handler.
while(!isset($threads) || $threads > 0)
{
curl_multi_exec($cm, $threads);
}
// loop through the result and close nicely.
for($i=0;$i<$total;$i++)
{
// get the result and some useful information.
$body = curl_multi_getcontent($ch[$i]);
$info = curl_getinfo($ch[$i]);
// remove the handler and close it.
curl_multi_remove_handle($cm, $ch[$i]);
curl_close($ch[$i]);
// print some information/statistics.
printf("%s: %s (%ssec, %sKB, %s redirects)\n",
$info['http_code'],
$info['url'],
round($info['total_time'], 2),
round($info['size_download'] / 1024, 2),
$info['redirect_count']
);
}
// close the multi-handler.
curl_multi_close($cm);
?>
Detta script kommer köra alla 5 förfrågningar samtidigt och därefter skriva ut lite information om varje URL. Med funktionen curl_getinfo() får vi ut användbar information om själva förfrågan till servern samt lite meta-data så som storlek, kodning, osv. Om du har PHP installerat för CLI, så kan du köra det direkt i shellet.
php ./curl-multi.php 200: http://www.ifconfig.se/ (0.48sec, 56.07KB, 0 redirects) 200: http://www.facebook.com/common/browser.php (1.08sec, 11.24KB, 1 redirects) 200: http://twitter.com/ (1.9sec, 43.29KB, 1 redirects) 200: http://www.youtube.com/ (0.62sec, 79.82KB, 0 redirects) 200: http://www.google.se/ (0.62sec, 10.54KB, 1 redirects)
Avslut
Att använda libcurl gör att man får enormt mycket kontroll över själva filhämtningen samtidigt som det är smidigt att konfigurera. Det stödjer dessutom enormt många protokoll så som HTTP, FTP, SSH/SCP, IMAP, POP3, m.fl.
En annan smidig funktion med libcurl är att oavsett om du hämtar en fil som är komprimerad (gzip, deflate, etc), så kommer alla cURL-funktioner ändå returnera den okomprimerade filen. Detta gör att man enkelt kan ignorera hela processen med att kontrollera om filen är komprimerad eller inte.
Hur använder du själv cURL och framför allt, i vilket språk använder du det i? Personligen har jag främst använt cURL ihop med PHP, då många av de andra språken jag använder har egna funktioner för just det jag behöver.

Bra och nyttig genomgång! Jag läste vidare och hittade två PHP-bibliotek som gör det enklare: http://code.google.com/p/rolling-curl/ och https://github.com/jmathai/php-multi-curl