Cuando escucho que alguien usa curl_multi_exec, por lo general resulta que solo lo cargan con, digamos, 100 URL, luego espera cuando todo se completa y luego los procesa todo, y luego vuelve a empezar con las siguientes 100 URL ... Culpa Yo también estaba haciéndolo, pero luego descubrí que es posible quitar/agregar identificadores a curl_multi mientras algo está todavía en progreso, y realmente ahorra mucho tiempo, especialmente si reutilizas las conexiones ya abiertas. Escribí una pequeña biblioteca para manejar la cola de solicitudes con devoluciones de llamadas; No estoy publicar la versión completa aquí, por supuesto ("pequeño" es todavía un poco de código), pero aquí es una versión simplificada de lo principal para darle la idea general:
public function launch() {
$channels = $freeChannels = array_fill(0, $this->maxConnections, NULL);
$activeJobs = array();
$running = 0;
do {
// pick jobs for free channels:
while (!(empty($freeChannels) || empty($this->jobQueue))) {
// take free channel, (re)init curl handle and let
// queued object set options
$chId = key($freeChannels);
if (empty($channels[$chId])) {
$channels[$chId] = curl_init();
}
$job = array_pop($this->jobQueue);
$job->init($channels[$chId]);
curl_multi_add_handle($this->master, $channels[$chId]);
$activeJobs[$chId] = $job;
unset($freeChannels[$chId]);
}
$pending = count($activeJobs);
// launch them:
if ($pending > 0) {
while(($mrc = curl_multi_exec($this->master, $running)) == CURLM_CALL_MULTI_PERFORM);
// poke it while it wants
curl_multi_select($this->master);
// wait for some activity, don't eat CPU
while ($running < $pending && ($info = curl_multi_info_read($this->master))) {
// some connection(s) finished, locate that job and run response handler:
$pending--;
$chId = array_search($info['handle'], $channels);
$content = curl_multi_getcontent($channels[$chId]);
curl_multi_remove_handle($this->master, $channels[$chId]);
$freeChannels[$chId] = NULL;
// free up this channel
if (!array_key_exists($chId, $activeJobs)) {
// impossible, but...
continue;
}
$activeJobs[$chId]->onComplete($content);
unset($activeJobs[$chId]);
}
}
} while (($running > 0 && $mrc == CURLM_OK) || !empty($this->jobQueue));
}
En mi versión $ los trabajos son en realidad de clase separada, no instancias de controladores o modelos. Solo manejan la configuración de las opciones de cURL, analizan la respuesta y llaman a una devolución de llamada dada en Completo. Con esta estructura, las nuevas solicitudes comenzarán tan pronto como finalice algo fuera de la agrupación.
Por supuesto que no te ahorra mucho si no solo recuperar lleva tiempo sino que también procesa ... Y no es un verdadero manejo en paralelo. Pero todavía espero que ayude. :)
P.S. hizo un truco para mí :) Una vez el trabajo de 8 horas ahora se completa en 3-4 minutos usando un grupo de 50 conexiones. No puedo describir ese sentimiento. :) Realmente no esperaba que funcionara como estaba planeado, porque con PHP raramente funciona exactamente como se suponía ... Eso fue como "ok, espero que termine en al menos una hora ... Qué ... Espera ... .¡Ya!? 8-O "
PHP no está diseñado para iniciar múltiples procesos. ¿Por qué no mirar un lenguaje como Python para lograr esto? – afuzzyllama
@afuzzyllama Es solo un submódulo, todo el proyecto está en PHP –
[nodejs] (http://nodejs.org) sería perfecto para esto. – Xeoncross