Tutoriel Vidéo Laravel Les files d'attente

Télécharger la vidéo Télécharger les sources

Losque l'on crée une application il y a parfois des traitements longs à effectuer. Malheureusement, la nature "synchrone" de PHP, fait que ces opérations vont bloquer le process. La mise en place d'un système de file d'attente va permettre de déléguer une partie des traitements à un processus séparé et ainsi d'améliorer les performances de l'application.

Par exemple lorsqu'un utilisateur upload un avatar on a besoin de générer plusieurs format. On pourrait être tenté de mettre le code dans notre controller.

public function store (Request $request) {
  $file = $request->file('avatar')->move('uploads', Auth::user()->id . '.jpg');
  $avatar = new Avatar($file->getRealPath());
  $avatar->generateThumbs(); // Redimensionne l'image en X formats
  return view('success');
}

Créer une tâche

Le problème est alors que l'utilisateur doit attendre que le serveur effectue les redimensionnements avant d'obtenir sa réponse. On va donc créer un "Job" pour effectuer notre traitement.

php artisan make:job ImageResize

Cette commande va permettre de générer une classe qui contiendra 2 méthodes :

  • Le constructeur, dans lequel on injectera les objets nécessaires au déroulement de la tâche
  • Une méthode handle() qui contiendra le code à effectuer lors du traitement de cette tâche
<?php

namespace App\Jobs;

use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class ImageResize implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    protected $image;

    /**
     * On crée une nouvelle instance.
     *
     * @param  Image  $image
     * @return void
     */
    public function __construct(Image $image)
    {
        $this->image = $image;
    }

    /**
     * Exécute la tache.
     *
     * @return void
     */
    public function handle()
    {
        $this->image->generateThumbs();
    }
}

Le trait SerializesModels n'est pas utile ici mais permet de serialiser les modèles Eloquent injectés en ne stockant que l'id. Le modèle sera de nouveau récupéré lors de l'éxécution de la tâche afin d'éviter les problèmes de serialisation. De la même manière vous ne pouvez pas stocker de données binaires (comme des fichiers) et il faudra les convertir en base64. En résumé il faudra essayer d'injecter des informations simples au niveau du constructeur afin d'éviter au maxium les problèmes.

Exécuter la tâche

Maintenant que notre tâche est créée on peut demander son placement en file d'attente gràce à la fonction globale dispatch(). Cette fonction prend en paramètre une instance de notre Job.

// On remplace notre $avatar->generateThumbs();
dispatch(new ImageResize($image));

Par défaut, ce code va éxécuter notre tâche directement. Il va falloir spécifier le système de file d'attente à utiliser en éditant la configuration queue.php. Laravel supporte les systèmes suivants :

  • beanstalkd, Beanstalk
  • sqsAmazon SQS
  • redis, Redis
  • database, Base de données
  • sync, Synchrone (les tâches sont éxécuté de manière synchrone)

Ces systèmes vont permettre de gérer la file d'attente et de stocker les tâche à effectuer. Pour traiter les tâches il faut utiliser la commandes

php artisan queue:work --tries=5 --timeout=30

Cette commande prend d'autres arguments que vous pouvez consulter en utilisant --help.

Que se passe-t-il en cas d'échec ?

Si une tâche venait à ne pas fonctionner comme attendue elle sera alors automatiquement placée dans une table failed_jobs dans votre base de données. Pour préparer cette table vous pouvez créer la migration à l'aide de la commande.

php artisan queue:failed-table
php artisan migrate

Vous pouvez lister les tâches qui ont échouées en utilisant cette table ou y accéder depuis la commande artisan

php artisan queue:failed

Vous pourrez ensuite choisir de replacer les tâches dans la fille d'attente

php artisan queue:retry <ID DE LA TACHE>
# Pour tout relancer
php artisan queue:retry all

ou les supprimer

php artisan queue:forget <ID DE LA TACHE>
# Pour tout supprimer
php artisan queue:flush 

Enfin, si c'est votre processus qui crash il vous faudra le redémarrer. Vous pouvez le faire de manière automatique à l'aide d'un supervisor.

D'ailleurs, lorsque vous déployez une nouvelle version de votre application, il faudra penser à redémarrer votre process.

php artisan queue:restart

Options supplémentaires

Une tâche possède des méthodes supplémentaires pour mieux contrôler son déroulement.

delay()

Permet de spécifier dans combien de temps la tâche devra être traitée

dispatch((new ImageResize($image))->delay(Carbon::now()->addSeconds(15));

onQueue()

Permet d'envoyer la tâche dans une file d'attente particulière

dispatch((new ImageResize($image))->onQueue('urgent'));

Cela permet de distribuer les tâches ou d'établir un système de priorité :

php artisan queue:work --queue=urgent,default