in symfony messenger rabbitmq sqs redis ~ read.

Symfony messenger + RabbitMq / SQS / Redis

PRÉSENTATION GÉNÉRALE

Hello Hello sf community...Dans ce billet je vais vous présenter un petit retour d'expérience qui a pour objectif de comprendre:
      — Comment mettre en place une architecture de conteneur Docker qui répond à notre besoin.
      — Fonctionnement de l'ordonnancement à travers RabbitMq.
      — Fonctionnement de Symfony Messenger.
      — Le couplage Symfony Messenger et RabbitMQ, SQS, Redis...afin d'envoyer des emails d'une façon asynchrone.
De nombreux traitements web pourraient être assez lourd, ce qui prend parfois beaucoup de temps soit à cause d'un trafic intense, soit à cause d'une mauvaise architecture logicielle, soit à cause de la mauvaise gestion de mémoire. Il y a des gens qui préfèrent augmenter la mémoire ou bien mettre un système de `balance loader` ...Mais ca résout pas le problème principal.

Bref, pour résoudre ce problème, on fait retour souvent à un mécanisme de file d'attente (ex: Standard Queue, FIFO Queue) pour effectuer des tâches chronophages et d’une manière ordonnancée et plus performante.

# Ordonnancement RabbitMQ
RabbitMQ permet de gérer le problème à travers un système des files de messages afin de permettre à différents clients de communiquer très simplement et plus efficacement d'une facon asynchrone en s'appuyant sur le protocole AMQP. C’est un gestionnaire de queues, permettant d’à synchroniser différents traitements. C’est pour assurer la haute disponibilité du serveur, dédié aux exécutions de traitements lourds comme l’envoi des e-mails, l’exportation, l'importation des fichiers, les attaques dos etc...
# Le composant Messenger (disponible à partir la version symfony 4.1)
Le composant messenger permet à l'application d'envoyer et de recevoir des messages vers / depuis d'autres applications ou via un système de queue basée sur un message bus. Le bus a pour rôle de dispatcher le message et d’exécuter l'handler approprié. Durant ce processus le message va passer dans une pile ordonnée de notre middleware. Le composant nous fournit aussi des fonctionnalités de routage pour intercepter les messages et les router vers le bon Transporter déjà défini au niveau de notre configuration au-dessous de `config.yml`.
# Architecture logicielle

Le composant est composé par différents éléments qui respecte le principe de single responsabilité (poo solid) :

Message : Objet PHP qui doit être sérialisable qui reprèsente un DTO (Data transfer object).
Message Handler : C’est ici qu’il pourra y avoir et exécuter la logique métier applicable aux messages.
Message Bus : C'est le composant qui a pour role de dispatcher notre message (la sérialisation et de la désérialisation messages), définir et exécuter le handler coresspondant associer à notre message.
D'ailleurs, le message Bus représente une approche de développement basé sur une architecture d'event sourcing ce qu'on appelle CQRS. L'une des architectures les plus recommandées qui résoudre énormément de problèmes de performance et de complexité algorithmique.
Worker Queue: Consomme les messages depuis les transports.
Pourquoi utiliser un transport?
Tronsport : Il permet de faire transiter les messages via différents brocker (ex: RabbitMQ, Kafka, Amazon SQS, etc.) Au cas où nous préferons traiter les messages d'une manière asynchrone.
À la place d'envoyer notre message d'une manière synchrone directement vers notre handler, on dirige le message vers un transporter pour le fournir (d'une façon asynchrone) à notre Handler. C'est cool !

Le composant bénéfice d’un panel sur la toolbar de debug, mais aussi d’une commande afin de pouvoir consommer les messages à travers la commande suivante: ` bin/console messenger:consume-messages 'nom de Tronsport(amqp, default)'`

Ok on y va !! … Nous allons présenter maintenant comment utiliser le compossant Messenger de Symfony pour pouvoir mettre en place un système d'envoi de mail d'une facon asynchrone.
Imganions si nous devons enovyer à 60000 utilisateurs un mail de notification, évidament la page correspendante à notre route va continuer à charger tantque le boucle for ($i = 1; $i <= 60000; $i++) n'a pas terminé son parcours, et du coup ca va consommer énormement de mémoire et de plus ce n'est pas pratique ni supportable en terme de User Experience

## First step : La MISE en palce de l'environnement
Pour se faire, nous avons deux choix :
### La 1ere Méthode: Instalation direct sur notre machine local
1. Installer rabbitmq et lancer la commande rabbitmq-server
2. rabbitmqctl status
3. rabbitmq-plugins enable rabbitmq_management
4. Activer l'extension amqp. : Il faudrait lancer 'apt install php-amqp' et ajouter 'extension = amqp.so' sous le fichier php.ini
5.`composer create-project symfony/skeleton my-project` # c'est mieux de travailler avec le repo `symfony/skeleton` vu qu'il est plus légère avec le minimum de dépendances possibles. Après on pourrait installer à fur et aux mesures les composants nécessaires.

La mise en place des dépendances necessaires:
composer req messenger annotation serializer twig mailer && composer req web-server profiler --dev

### La 2ème Méthode: utiliser un système de conteneurs

On prépare notre docker-compose.yml pour regroupper les différents configurations nécessaires. Le principal intérêt est d’assembler les briques de notre application en conteneurs pouvant être partagés, sous forme d’images, de telle sorte que chaque image représente un stack bien déterminé.
Voici la configuration appropriée docker-compose.yml:

## Second step: Implémentation des classes avec les services nécessaires
La création de notre Message : `SendNotification`

Nous allons utlisé notre command Bus pour dispatcher notre command, autrement dit notre message de type SendNotification pour que l'handler sera notifié de cette action et répondre à travers le traitement nécessaire.
Voici notre controlleur:

La création de notre Handler: (par convention faudrait que le nom de la callse soit 'nom de la classe de notre message'**+** Handler. Pour traiter un message il faudra créer un handler avec une méthode `__invoke`
Ensuite, il faudrait enregistrer notre `Handler` en tant que service avec le tag `messenger.message_handler`:

On pourrait aussi lancer la commande bin\console debug:messenger c'est une commande CLI pour exposer les classes de message pouvant être dispatché à travers notre Messenger.

Le message doit maintenant être traité de manière synchrone, c'est à dire, lorsque on consulte notre homepage, le courrier électronique sera immédiatement traité.
Nous voulons reporter l'envoi de l'e-mail. Au lieu d’appeler directement notre Handler, nous voulons bien acheminer les messages vers un ou plusieurs expéditeurs/transports:

La dernière étape consiste donc à configurer le transport.

`.nev` : Ici c'est l'endroit ou on définit notre variable d'environnement MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages Une fois que les messages ont été acheminés correctement, nous pouvons les consommer avec la commande $ bin/console messenger:consume-messages messenger.default_receiver.

Pour automatiser le mécanisme, nous allons utiliser Supervisor afin exécuter la commande d'une façon automatique -> -> ->

## Third step: Supervisor
Pour les gens qui ne connaissent pas `supervisor`, il reprèsente un système de contrôle des processus/services applicatifs destiné aux systèmes de types UNIX. Nous on va l'utiliser pour relancer en permance les consumers.

Bref, après avoir installer apt-get install supervisor nous allons mettre en place la configuration convenable en utilisant le fichier /etc/supervisor/supervisord.conf:

Il faudrait prendre en considération que le lancement de service supervisor arrête le service `php-fpm`, c'est pour cela que nous devons ajouter au niveau de la configuration qui concerne le relancement de `php-fpm` en cas d'arrêt.
N'oublions pas de lancer la commandeservice supervisor start. L'outils bénificie aussi de sa propre commande supervisorctl affin d'administrer les différents services gérés par supervisor. I Il fournit également une interface web (http://127.0.0.1:9001) qui est activable via la section [inet_http_server] sous `supervisord.conf` et qui permet de gérer les services de la même façon que la ligne de commande.
### Last step: Configuration de notre mailer
Nous avons commenté le mail spool parceque nous utilisons déja un transporteur amqp pour enchainer l'envoi d'une facon asynchrone donc ya pas besoin de traiter le processus avec un système de spooling.

# SQS(Simple Queue Service) Amazon
Tout d'abord commencons par la création d'un service SQS à partir de SQS Managment Console et récupérer le nom la région et les clés d'authentification qui se trouvent sous l'interface IAM.

Après il suffit d'installer les dépedences necessaires:
composer require messenger enqueue/messenger-adapter enqueue/sqs pour pouvoir utiliser d'autres type de transports nous avons fait recours à ce package https://github.com/php-enqueue/messenger-adapter qui va nous fournir différents adapteurs de plus autres que amqp.

Voici les différentes configurations nécessaires:

Afin de traiter les messages, il suffit de lancer la commande: `$ php bin/console messenger:consume sqs`

Nous pouvons visualiser, lire le continue des messages et purger la queue à travers l'interface offerte par AWS SQS:


# REDIS
Pour les gens qui prèfère `memcached`, on pourrait suivre le meme démarche.
1) Instalation des dépendances: $ composer require enqueue/redis predis/predis:^1 2) Configuration notre DNS:
On pourrait connecter à notre CLI redis à travers une simple `docker compose exec redis redis-cli` Après nous allons remarquer la création automatique d'une CLE de type list `messages`.
`lrange messages 0 -1 `: C'est pour récupérer tout les messages sauvegardés. Et finallement il suffit de lancer la commande: `$ php bin/console messenger:consume redis` pour pouvoir connsomer les différents messages.

That's it.