Bonjour à tous,
Je découvre actuellement Symfony, et en particulier le composent Messenger, que je trouve particulièrement pratique.
Dans le cadre de la création d'un petit site e-commerce, j'ai suivi différents tutoriaux pour mettre en place l'envoi d'email de confirmation d'une commande en asyncrone. En local, avec wamp64, c'est parfait. Tout fonctionne et les emails partent bien après la fameuse commande 'symfony console messenger:consume async -vv'.
Seulement, les choses se compliquent pour moi au moment de passer en prod. Peut-on vraiment utiliser ce composant quand on a un "simple" hébergement pro chez OVH ? Car on ne peut pas utiliser Supervisor ou systemd sur cet hébergement (ou alors, je ne sais pas comment faire). Je me suis dit, un peu naïvement, qu'appeller la commande à travers un controller que je mettrais en suite en cron suffirait. Alors j'ai écrit le code ci-après (d'après celui proposer à ce lien) :
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route;
define('STDIN',fopen("php://stdin","r"));
class MessengerConsumeController extends AbstractController
{
/**
* @Route("/mc", name="sendEmailOrderByBus")
* Cette route sert pour mes tests à travers le navigateur
* (mc = Messenger Consumme)
*/
public function messengerConsume(KernelInterface $kernel): Response
{
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'messenger:consume',
'async',
'-vv',
]);
// You can use NullOutput() if you don't need the output
$output = new BufferedOutput();
$application->run($input, $output);
// return the output, don't use if you used NullOutput()
$content = $output->fetch();
// return new Response(""), if you used NullOutput()
return new Response($content);
}
}
Quand je test ce controller dans le navigateur, cela fonctionne 'presque' : les emails s'envoient bien, mais la page charge sans s'arrêter, jusqu'à finir sur un code 500.
En faisant un cron qui appelle ce fichier, les emails ne partent pas, j'ai directement un log d'erreur qui stipule que la page contient du code qui n'abouti pas.
En désespoir de cause, j'ai tester un cron sur ce fichier que j'ai mis dans le dossier public/
namespace App\Command;
use Doctrine\Bundle\DoctrineBundle\Command\Command;
use Symfony\Component\Process\Process;
class MessengerConsumeCommand extends Command
{
protected static $defaultName = "messenger:consume";
protected function configure()
{
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$process = new Process(['ls', '-lsa']);
$process->start();
foreach($process as $type => $data) {
if ($process::OUT === $type) {
echo "\nDebug : ".$data;
} else {
echo "\nErreur : ".$data;
}
}
return 0;
}
}
sans résultat (ni erreur ni réussite).
J'ai alors testé ça, placé à la racine du site
/** @var Composer\Autoload\ClassLoader */
require __DIR__.'/../vendor/autoload.php';
use Composer\Autoload\ClassLoader;
use Symfony\Bundle\FrameworkBundle\Console\Application;
$output = shell_exec('cd .. ;php bin/console --env=prod messenger:consume async --vv');
// Ici tu peux par exemple créer un ficher log dans /var/cron/ et mettre le résultat de output, mettre dans un try and catch et lever une exception etc...
exit;
Mais j'ai un log d'erreur qui me dit que la commande php n'est pas reconnu. Idem si je remplace 'php bin/console' par 'symfony console' : la commande symfony n'est pas reconnu.
Je précise bien que j'ai testé ces deux derniers fichiers en novice. Vous avez le droit de vous moquer de moi si c'était une idée ridicule d'amateur.
Bref, tout ça pour demander : est-il possible d'utiliser le composant Messenger avec un hébergement mutualisé ? Si oui, comment ? Ou bien le défi est-il trop grand, et ce composant reste le privilège de ceux qui ont un serveur dédié ?
En vous remerciant par avance de vos réponses,
Cordialement.
Bonjour, je ne suis pas forcément le mieux placer pour répondre mais comme personne ne vous répond, je vais vous partager mes recherches car je suis dans le même cas que vous. Effectivement sur un hébergement OVH non dédié / non VPS nous ne sommes pas sudoer pour supervisor et n'avons pas accès à systemd. On ne peut donc pas mettre en place l'une des 2 solutions de processus proposées par la doc Symfony : https://symfony.com/doc/current/messenger.html.
Pour l'utilisation d'une CRON, je suis tombé sur cette proposition : https://www.devcoder.xyz/blog/516495/use-symfony-messenger-without-supervisor-3cl6, que je vais tester.
Il y a aussi l'option d'envoyer une 2ème requête asynchrone pour compléter la 1ere, sans en attendre la réponse, plutôt que de passer par un bus : https://afup.org/talks/3172-symfony-httpclient-vs-guzzle-vs-httplug.
Ceci dit votre appel a une commande par CRON me parait étrange. J'utilise un fichier bash (.sh) de ce type pour lancer mes CRON :
#!/bin/bash
export $(grep -v "^#" .env.local | xargs)
# Force source bash profile to update PATH
source ~/.bash_profile
source ~/.bashrc
ovhConfig
php ~/{site-dir}/bin/console {commande:name}
Bonjour,
Merci pour votre réponse, je commençais à croire que je n'étais pas au bon endroit pour espérer une petite porte de secours à mon problème.
Je testerai dès que possible les solutions que vous proposez (je suis actuellement sur un autre dossier) et vous ferez un retour.
Et puis, ne prenez pas en compte la commande CRON que j'avais posté. Je l'avais intégré dans mon sujet en désespoire de cause, mais elle n'a pas de sens. Merci d'avoir partagé votre fichier bash !
Vous souhaitant une bonne journée,
Cordialement.
messenger sans supervisor
çà pourrait resoudre ton probleme:
<?php
namespace App\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Event\WorkerRunningEvent;
/**
@package App\Event
*/
class CronRunningEvent implements EventSubscriberInterface
{
public function onWorkerRunning(WorkerRunningEvent $event): void
{
if ($event->isWorkerIdle()) {
$event->getWorker()->stop();
}
}
/**
Puis un cron toute les minutes
Oui il me semble bien que messenger ne peut pas fonctionner sur un mutualisé car ils n'aiment pas les tâches longues. Ma première piste serait aussi une tâche récurrente (CRON) qui utilise la commande consume symfony console messenger:consume async
mais si il y a beaucoup de tâche et que ça prend du temps ça risque d'être bloqué :(
Pardon pour ma réponse tardive, j'étais à l'étranger et est dû mettre ma vie "informatique" entre parenthèse. Je reviens aujourd'hui, en pleine forme pour reprendre mes codages !
Merci pour vos réponses. Un des problèmes que je rencontre est effectivement pour la tâche CRON. Pour ces derniers cas, les mutualisés d'OVH ne permettent que des appels de fichiers tous les x minutes (60 minutes étant le minimum, mais ça me convient). Je ne peux pas faire une ligne de commande en CRON.
Quand, dans l'adresse d'un navigateur, je lance la route relative au nom "sendEmailOrderByBus" (le premier code que j'ai posté), Messanger envoie bien les messages stockés en BDD, mais après un long processus de chargement, le navigateur termine sur une page avec Erreur 500. Donc, quand fais un CRON sur ce fichier, OVH fini par le désactiver en pretextant que le code comporte une boucle infinie. Je me disais qu'il y avait peut-être moyen de limiter le temps d'action de la fonction...
Bref, je vais finir par conclure qu'il n'y a pas de solution avec un hébergement mutualisé. Ca m'étonne que Symfony n'est pas prévu ce cas, mais je dois me rendre à l'évidence. Tant pis. Je vais toutefois attendre un peu avant de m'avouer vaincu.
Dans tous les cas, merci pour vos propositions,
Bonne journée.