Traitements asynchrones
1Présentation
Temma propose Asynk, un moyen simple d'effectuer des traitements asynchrones. Ce système est basé sur l'utilisation du composant d'injection de dépendances, en l'étendant pour que les appels aux objets gérés par le composant soient traités de manière asynchrone.
Exemple :
// appel synchrone via le loader
$this->_loader->MonObjet->maMethode($param1, $param2);
// appel identique en asynchrone
$this->_loader->asynk->MonObjet->maMethode($param1, $param2);
Comme pour le composant d'injection de dépendances, il est possible d'utiliser une notation de type tableau pour exécuter un objet placé dans un namespace :
// appel asynchrone
$this->_loader->asynk['\Mon\Name\Space\MonObjet']->maMethode($param1, $param2);
Les tâches peuvent être exécutées de plusieurs manières différentes :
- par des workers Temma (des programmes qui tournent en tâche de fond)
- par un script Temma exécuté par crontab toutes les minutes
- par un script Temma exécuté par xinetd à chaque fois qu'une tâche est lancée
Pour les exécutions par worker ou par crontab, les tâches peuvent être récupérées depuis une file de messages (Beanstalkd ou AWS SQS) ou une base de données (MySQL ou Redis). Pour l'exécution par xinetd, seuls les stockages en base de données (MysQL ou Redis) sont possibles.
Attention aux limites concernant les données stockées pour chaque tâche :
- 16 MO pour MySQL
- 512 MO pour Redis
- 64 KO pour Beanstalk
- 512 KO pour SQS
Beanstalkd est la file de messages conseillée pour l'exécution de tâches avec Asynk.
Vous pouvez aussi utiliser une file Amazon SQS, mais son fonctionnement implique que les workers se connectent régulièrement
pour voir s'il y a des tâches en attente, sachant qu'une facturation s'applique à chaque reqête effectuée.
La solution la plus simple à mettre en œuvre est de stocker les tâches en base de données, et de les exécuter par crontab. Pour une exécution rapide des tâches, au plus proche du temps réel, il est conseillé d'ajouter une exécution par serveur xinetd.
2Configuration
2.1Principe
La configuration repose sur une configuration étendue x-asynk dans le fichier etc/temma.php, contenant deux paramètres :
-
transport : le nom de la source de données qui va servir à transmettre
les tâches.
Le comportement sera différent en fonction du type de la source de données : - storage : le nom de la source de données qui va stocker les messages en attendant leur traitement, dans le cas où il n'y a pas de file de messages :
Suivant les valeurs, l'un ou l'autre paramètre peut être optionnel.
Voici les combinaisons possibles :
transport | storage | Traitement |
---|---|---|
MySQL | Traitement par crontab ou par workers | |
Redis | ||
socket | MySQL | Traitement par xinetd (+ crontab en option) |
socket | Redis | |
Beanstalk | Traitement par workers | |
SQS |
Asynk écrit des messages de log en utilisant la classe de log Temma/Asynk.
Voici un exemple de fichier de configuration :
<?php
return [
'application' => [
// sources de données
'dataSources' => [
// file de messages Amazon SQS
'sqs' => 'sqs://AKXYZ:PWD@sqs.eu-west-3.amazonaws.com/123456789012/queue_name',
]
],
// seuils des messages de log
'loglevels' => [
'Temma/Base' => 'ERR',
'Temma/Web' => 'WARN',
'Temma/Asynk' => 'NOTE',
],
// configuration Asynk
'x-asynk' => [
// transport : file SQS
'transport' => 'sqs',
],
];
2.2Stockage MySQL
Si vous choisissez un stockage des tâches dans une base de données MySQL, il faut créer la table qui contiendra les tâches.
Voici la requête de création de cette table :
CREATE TABLE Task (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
dateCreation DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
dateUpdate DATETIME ON UPDATE CURRENT_TIMESTAMP,
status ENUM('waiting', 'reserved', 'processing', 'error') NOT NULL DEFAULT 'waiting',
token CHAR(16) CHARACTER SET ascii COLLATE ascii_general_ci,
target TINYTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
action TINYTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
data MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
PRIMARY KEY (id),
INDEX status (status),
INDEX token (token)
);
La clé primaire (champ id) peut être du type BIGINT si vous avez besoin de gérer plus de 4 milliards de tâches.
Si la table est stockée dans une base différente de celle de la connexion, ou si elle est nommée autrement que Task, ou que les champs ont d'autres noms, il est possible de le spécifier dans la configuration étendue x-asynk du fichier etc/temma.php.
<?php
return [
'x-asynk' => [
'base' => 'asynk_db',
'table' => 'asynk_tasks',
'id' => 'task_id',
'status' => 'task_status',
'token' => 'task_token',
'target' => 'task_target',
'action' => 'task_token',
'data' => 'task_data',
]
];
- Ligne 5 : Nom de la base de données qui contient la table.
- Ligne 6 : Nom de la table.
- Ligne 7 : Nom du champ contenant la clé primaire.
- Ligne 8 : Nom du champ contenant le statut.
- Ligne 9 : Nom du champ contenant le jeton de réservation.
- Ligne 10 : Nom du champ contenant l'objet cible.
- Ligne 11 : Nom du champ contenant la méthode cible.
- Ligne 12 : Nom du champ contenant la sérialisation des paramètres.
Il est aussi possible d'utiliser une DAO personnalisée :
<?php
return [
'x-asynk' => [
'dao' => '\MyApp\AsynkDao'
]
];
3Traitement par crontab
3.1Présentation de la crontab
Le traitement par crontab est très simple à mettre en œuvre. Le démon crontab est installé ou installable sur tous les systèmes Unix, et permet d'être indépendant de tout autre logiciel comme une file de messages.
Le traitement par crontab est lancé toutes les minutes. Cela peut amener un léger délai dans le traitement des tâches. Si vous avez besoin d'un traitement sans latence, vous pouvez ajouter un traitement par xinetd (voir plus bas).
3.2Configuration de Temma pour la crontab
Le stockage utilisé pour enregistrer les tâches doit être déclaré dans la configuration étendue x-asynk.
Exemple de fichier etc/temma.php, avec un stockage Redis :
<?php
return [
'application' => [
'dataSources' => [
'ndb' => 'redis://localhost'
]
],
'x-asynk' => [
'storage' => 'ndb'
],
];
3.3Configuration de la crontab
Pour configurer la crontab, vous avez deux possibilités :
-
Modifier le contenu du fichier etc/asynk/crontab pour adapter le
chemin vers la racine de votre projet.
Ensuite, copier ce fichier dans /etc/cron.d/ (sous un nom adapté), par exemple avec la commande :
sudo cp /chemin/vers/projet/etc/asynk/crontab /etc/cron.d/mon_projet
-
Ou ajouter la ligne suivante (en adaptant le chemin) dans la crontab de l'utilisateur désiré :
* * * * * cd /chemin/vers/projet/; bin/comma AsynkWorker crontab
4Traitement par xinetd
4.1Présentation de xinetd
Xinetd est un "super-démon" qui écoute sur plusieurs ports réseau. À chaque fois qu'il reçoit une connexion entrante,
il lance le programme associé, et se charge des échanges réseau.
Pour la gestion d'Asynk, xinetd écoute par défaut sur le port 11137.
Lorsque Asynk utilise xinetd, les tâches asynchrones sont traitées immédiatement. Il y a toutefois deux choses à prendre en considération :
- Si vous devez traiter un très grand nombre de tâches simultanées, xinetd montrera sa limite. Il est alors conseillé de passer par une file de message (Beanstalkd ou SQS).
- Il peut arriver que xinetd ne puisse pas traiter certaines tâches (le démon ne tourne pas ou il est saturé). Il est donc conseillé de conjuguer le traitement par xinetd avec le traitement par crontab (voir plus haut), afin que les tâches qui n'ont pas été traitées par xinetd le soient par la crontab.
4.2Configuration de Temma pour xinetd
Dans la configuration, il faut prévoir une source de données de type socket, qui servira a Asynk pour se connecter à xinetd. Le serveur xinetd peut être sur le serveur local ou sur une machine distante. Par défaut, le port de connexion utilisé par Asynk est le 11137.
Exemple de fichier etc/temma.php, avec un stockage MySQL :
<?php
return [
'application' => [
'dataSources' => [
'sock' => 'tcp://localhost:11137',
'db' => 'mysql://user:password@localhost'
]
],
'x-asynk' => [
'transport' => 'sock',
'storage' => 'db'
],
];
4.3Configuration de xinetd
Pour configurer xinetd, modifiez le fichier etc/asynk/xinetd
pour adapter le chemin, puis copiez-le dans le répertoire /etc/xinetd.d/
(sous un nom adapté), par exemple avec la commande :
sudo cp /chemin/vers/projet/etc/asynk/xinetd /etc/xinetd.d/mon_projet
4.4(optionnel) Configuration de la crontab
Utiliser la crontab en plus de xinetd permet de s'assurer que toutes les tâches soient traitées, même si xinetd ne tourne pas ou est saturé.
Pour configurer la crontab, vous avez deux possibilités :
-
Modifier le contenu du fichier etc/asynk/crontab pour adapter le
chemin vers la racine de votre projet.
Ensuite, copier ce fichier dans /etc/cron.d/ (sous un nom adapté), par exemple avec la commande :
sudo cp /chemin/vers/projet/etc/asynk/crontab /etc/cron.d/mon_projet
-
Ou ajouter la ligne suivante (en adaptant le chemin) dans la crontab de l'utilisateur désiré :
* * * * * cd /chemin/vers/projet/; bin/comma AsynkWorker crontab
5Traitement par workers
5.1Présentation des workers
Les workers sont des programmes qui tournent en tâche de fond. Vous pouvez faire tourner autant de workers que vous le souhaitez. Si un seul worker s'exécute, on peut considérer qu'il s'agit d'un démon de traitement.
Les workers se connectent à la source de données (file de message ou base de données) pour récupérer les tâches à effectuer. Ils récupèrent les tâches une à une, et les traitent séquentiellement. Si vous avez un grand nombre de tâches à traiter, il vaut donc mieux avoir plusieurs workers en parallèle, sinon les tâches pourraient s'empiler plus vite qu'elles ne sont traitées.
Il est important de s'assurer qu'un nombre minimal de workers tourne en permanence, pour ne pas risquer que des tâches ne soient pas traitées. Il est possible d'utiliser un superviseur comme Supervisord, qui redémarrera automatiquement les workers si le nombre minimal d'instances n'est pas assuré.
5.2Configuration de Temma pour les workers
La configuration Temma doit contenir des informations sur le transport et/ou le stockage des tâches.
Exemple de fichier etc/temma.php, avec un stockage MySQL :
<?php
return [
'application' => [
'dataSources' => [
'db' => 'mysql://user:password@localhost'
]
],
'x-asynk' => [
'storage' => 'db'
],
];
Autre exemple de fichier etc/temma.php, avec un transport Beanstalkd :
<?php
return [
'application' => [
'dataSources' => [
'beanstalk' => 'beanstalk://localhost/tube3'
]
],
'x-asynk' => [
'transport' => 'beanstalk'
],
];
Par défaut, les workers qui font une écoute active (“polling”) attendent 60 secondes entre deux connections pour récupérer des tâches en attente. Cela concerne les workers qui se connectent à une file Amazon SQS ou à une base MySQL ou Redis ; cela ne concerne pas les files Beanstalkd.
Il est possible de modifier ce délai avec le paramètre loopDelay de la configuration étendue x-asynk :
<?php
return [
'application' => [
'dataSources' => [
'db' => 'mysql://user:password@localhost'
]
],
'x-asynk' => [
'storage' => 'db',
// délai de 90 secondes entre deux vérifications
'loopDelay' => 90
],
];
5.3Configuration de Supervisor
L'utilisation de Supervisor est optionnelle, mais elle peut être utile pour s'assurer que les workers tournent, et qu'ils sont relancés si un problème survenait.
Recopiez le fichier etc/asynk/supervisor.conf vers /etc/supervisor/conf.d/asynk.conf avec la commande suivante :
sudo cp /chemin/vers/projet/etc/asynk/supervisor.conf /etc/supervisor/conf.d/asynk.conf
Modifiez ensuite les paramètres suivants du fichier :
-
command : Indiquez le chemin correct vers la racine de votre projet.
Exemple : command=/chemin/vers/projet/bin/comma AsynkWorker -
numprocs : Indiquez le nombre de workers qui doivent s'exécuter en même temps.
Exemple : numprocs=5
Forcez ensuite Supervisor à prendre en compte cette configuration :
sudo supervisorctl reread
sudo supervisorctl update