Laravel 5 Events Handlers : Traitement rapide des jobs avec plusieurs tubes

Dans Laravel 5, l’utilisation des Queues pour les Commands, ou les Events Handlers est simplifiée au maximum grâce à l’interface ShouldBeQueued.
C’est extrêmement pratique, mais parfois, la file d’attente est beaucoup trop longue alors que l’on a encore pleins de ressources serveur disponibles.
L’avantage dans notre cas, c’est qu’on va un peu faker du Multithreading en utilisant le driver Beanstalk, mais ce n’est pas encore le sujet.

Le problème

Imaginons : votre application reçoit une liste de plusieurs dizaines de milliers d’adresses emails à importer. Vous avez une classe qui gère l’import, et qui met les tâches en file d’attente, pour les envoyer à votre fournisseur (Mailchimp ou autre).

Le souci, c’est qu’ils sont tous en attente (les jobs d’ajout d’adresse mail). Pendant plusieurs heures, toutes les autres tâches (jobs) ne seront pas exécutées. On ne peut pas envoyer un mail de confirmation 2h plus tard au client qui vient de commander. Et de toute façon, on n’a pas envie d’attendre x heures que tous les mails soient poussés, alors qu’on a un serveur qui rox du poney !

Ajouter des tubes (queues)

On va créer des « tubes » supplémentaires. Pour ça, il suffit d’aller dans /config/queue.php. Ajoutez vos tubes dans le tableau connections :

                'background1' => array(
			'driver' => 'beanstalkd',
			'host'   => 'localhost',
			'queue'  => 'background1',
		),
		'background2' => array(
			'driver' => 'beanstalkd',
			'host'   => 'localhost',
			'queue'  => 'background2',
		),
		'background3' => array(
			'driver' => 'beanstalkd',
			'host'   => 'localhost',
			'queue'  => 'background3',
		),

Dans cet exemple, on en ajoute 3 (en plus de la default) mais vous pouvez en mettre plus. Attention cependant à ne pas en mettre trop par rapport à la capacité du serveur, qui peut vite être à genoux si vous n’êtes pas cohérent.

Comment spécifier le tube pour un gestionnaire d’événement

Il suffit d’ajouter une méthode queue() à votre classe, comme ceci :

public function queue($queue, $event, $arguments)
    {
        $queue->push($event, $arguments, 'nom_du_tube');
    } 

C’est légèrement différent de ce qu’on a vu pour les Commands, mais ça reste très simple.

Dispatcher les instances d’événement dans les tubes

On a les 3 tubes, et 5000 adresses emails à envoyer, donc il faut répartir la distribution. Pour cela, on peut facilement utiliser un trait :

<?php namespace Acme\Queues;

trait DispatchInAvailableQueues {

    /**
    * Tell to the handler class where to push this job.
    *
    * @return void
    */
    public function queue($queue, $event, $arguments)
    {
        $queue->push($event, $arguments, 'background' . rand(1,3));
    }

}

Et maintenant, c’est très simple : il suffit d’implémenter l’interface ShouldBeQueued, et d’utiliser le trait DispatchInAvailableQueues que l’on vient juste de créer, comme ceci :

<?php namespace Acme\Handlers\Events;

use Illuminate\Contracts\Queue\ShouldBeQueued;
use Acme\Queues\DispatchInAvailableQueues;

class PushOptinOnProvider implements ShouldBeQueued{

	use DispatchInAvailableQueues;

	/**
	 * Create the event handler.
	 *
	 * @return void
	 */
	public function __construct()
	{
		//make stuff
	}

	/**
	 * Handle the event.
	 *
	 * @param  AccountIsUpdated  $event
	 * @return void
	 */
	public function handle(EventClass $event)
	{

		//make stuff

	}

}

Et c’est terminé.

Ce qui se passe

Etant donné que l’on utilise le trait DispatchInAvailableQueues, notre Handler bénéficie de la méthode queue() dont on a besoin pour changer le tube, et ne pas encombrer le principal.
Le trait se charge de distribuer de manière aléatoire dans les tubes de 1 à 3 grâce à rand(). Pensez donc à changer les nombres dans le trait si vous avez créé plus de 3 nouveaux tubes.
Si, pour un autre événement, vous avez besoin de dispatcher la charge, vous n’avez plus qu’à ajouter le trait à votre nouveau gestionnaire d’événement.

metrogeek

Laisser un commentaire