Traitements parallèles grâce a Gearman

Je me suis déjà frotté au multitâche avec PHP, et il faut dire, avec un certain recul et pas mal de temps de tests, que je suis plutot déçu par la stabilité du système mis en place. J’avais développé ça sur une base de forks de process. Ça marchait bien jusqu’à un certain point, lorsque le serveur était un peu chargé (à peine plus que d’habitude) certain de mes process plantaient aléatoirement, jamais le même process. Peut-être que j’abusais un peu, mais bon j’en ai besoin de mes 200 process moi.

Dans ma quête de stabilité, je me suis dit qu’utiliser Gearman serait une bonne idée, il est censé être simple, scalable, stable, et tout et tout.

Voici la structure mise en place :

  • Un client, qui en fait est un script PHP, qui envoie envoie des jobs (des tâches) au serveur gearman.
  • Un serveur gearman qui s’occupe d’empiler les jobs et de les distribuer au workers.
  • Plusieurs workers qui eux aussi sont des scripts PHP. Ils reçoivent les jobs que le client à envoyé au serveur. Une fois le job terminé, le worker qui avait le job en charge retourne le résultat au client via le serveur.

On verra pas la suite que le client n’est pas obligé d’attendre le résultat de ses jobs. Dans ce cas le client ne sait pas si le job s’est bien passé et il ne connaitra pas non plus le résultat du job.

Dans mon cas je n’ai pas besoin du résultat, j’ai juste besoin d’empiler des jobs afin de les lancer en parallèle et ce n’est pas la peine d’attendre que chaque job ait fini son travail.

Installation

L’installation décrite ici est pour Linux, pour Ubuntu plus exactement, voici la démarche a suivre :

Il faut ajouter ce dépôt à vos sources dans le fichier /etc/apt/sources.list :  https://launchpad.net/~gearman-developers/+archive/ppa

On met à jour puis on installe gearman avec apt-get :

apt-get update
apt-get upgrade
apt-get install gearman-job-server

On installe l’extension pour PHP

apt-get install php5-dev
apt-get install libgearman-dev
wget http://pecl.php.net/get/gearman-0.8.0.tgz
tar xzf gearman-X.Y.tgz
cd gearman-X.Y
phpize
./configure
make
make install

Si tout se passe bien, on a juste à a jouter l’extension au fichier php.ini :

extension="gearman.so"

Et on n’oublie pas de recharger son serveur web pour prendre en compte la nouvelle extension.

Pour vérifier que tout est bien en place, on peut utiliser cette commande :

print gearman_version();

Le client

Voila le code du client utilisé :

$client= new GearmanClient();
$client->addServer("127.0.0.1", 4730);
 
$task_id = 0;
while($image_id = get_new_image())
{
    $params = array(
        "db_config" => $db_config[$options['environment']],
        "image_id" = $image_id,
    );
 
    $client->addTaskBackground("process_image", serialize($params), null, "T".$task_id++);
}
$client->runTasks();
exit(0);

On commence par créer un objet GearmanClient, il se connectera à notre serveur grâce au addServer.

Dans cet exemple, on empile des traitements sur des images, c’est la fonction process_image qui sera appelée par chaque job.

On crée aussi un tableau de paramètres qui sera sérialisé avant d’être envoyé au job. Les paramètres que j’ai mis ici sont juste des exemples, on peut y mettre ce qu’on veut pourvu que ce qu’on y met soit sérialisable.

Le runTasks() achève le travail et indique au serveur de lancer les tâches.

Les workers

Pour gérer les workers, j’ai fait un petit script qui en lance un certain nombre à coup de fork().

echo "Starting {$nb_workers} workers : ";
for($i=0; $i<$nb_workers; $i++)
{
     $pid = pcntl_fork();
      if($pid == -1)
      {
          echo "Fork error !\n";
      }
      else if($pid)
      {
         $pids[] = $pid;
         echo "Worker pid : {$pid}\n";
      }
      else
      {
         call_user_func_array("run", array());
         exit(0);
     }
 }
 
 [...]
 
 function run()
 {
     $worker= new GearmanWorker();
     $worker->addServer("127.0.0.1", 4730);
     $worker->addFunction("process_image", "do_work");
     while($worker->work());
}
 
function do_work($job)
{
    $params = unserialize($job->workload());    
 
    // Code du traitement de l'image
}

A vous de l’adapter selon vos besoins, il n’y a la rien de bien compliqué.

Conclusion

Finalement, la mise en place d’un tel système s’avère assez simple et rapide. Il ne nous aura pas fallu plus d’une journée pour réaliser l’ensemble alors que nous n’avions jamais touché à Gearman auparavant.

La stabilité à l’air d’être au rendez-vous, nous n’avons eu aucun plantage de job ou autre depuis sa mise en tests et nous lançons environs 700 jobs par jours repartis sur 10 workers. C’est pas énorme mais on a prévu de charger un peu plus la bête dans un second temps.

Pour plus d’informations, voici quelques liens :

4 commentaires ↓

#1 Geekbay on 06.30.11 at 23:56

Ca a l’air pas mal du tout comme systeme, par contre dans ton code, comment tu fais le controle que la tache s’est bien deroulée et que donc toutes les images ont bien ete traitées?

#2 Stéphane on 07.01.11 at 8:52

Dans mon cas je n’ai pas besoin d’avoir le retour du traitement, c’est pour ça que j’utilise addTaskBackground() dans le client.

Pour attendre le résultat d’un worker, il faut utiliser addTask() couplé à un setCompleteCallback() :

$client->addTask("process_image", serialize($params), null, "T".$task_id++);
$client->setCompleteCallback("complete");
$gmclient->runTasks(); 

function complete($task)
{
  print "Terminé : " . $task->unique() . ", " . $task->data() ;
}

Voir la doc php c’est bien expliqué.

#3 À lire cette semaine 10 | www.freva.net on 07.02.11 at 10:43

[...] laquelle une extension pour PHP existe. Je vous invite à découvrir cette techno bien sympathique.Traitements parallèles grâce a GearmanNouveau moteur de recherche Google«Ca s’appelle WDYL, comme What Do You Love (from Google et [...]

#4 Bertrand Mansion on 07.12.11 at 21:00

Salut,
Nous aussi on s’amuse avec Gearman en ce moment. Personnellement, j’adore même s’il est un peu difficile à installer sur CentOS (j’ai dû compiler Boost et Gearman à partir des sources).

Ca m’intéresserait de lire la suite de votre test, à savoir si votre architecture tient le coup. De notre côté, on n’aura pas beaucoup de workers, peut-être 15 par serveurs et 3 serveurs de workers (donc 45 workers) mais ça sera des taches qui durent longtemps.

Je posterai un truc sur mon blog quand on aura mis tout ça en place.
Bonne continuation.

Laisser un commentaire