If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeMe encanta la posibilidad de aplazar el trabajo para más tarde enviando mensajes a un transporte, pero hay al menos un inconveniente práctico: hace que sea un poco más difícil desarrollar y codificar tu aplicación. Además de configurar tu servidor web, tu base de datos y todo lo demás, ahora tienes que acordarte de ejecutar:
php bin/console messenger:consume
De lo contrario... las cosas no funcionarán del todo. Si tienes una configuración robusta para el desarrollo local -quizás algo que utilice Docker- podrías incorporar esto a esa configuración para que se ejecute automáticamente. Salvo que... seguirías teniendo que acordarte de reiniciar el trabajador cada vez que hagas un cambio en algún código que utilice.
No es lo peor. Pero, si esto te vuelve loco, hay una solución muy buena: decirle a Messenger que maneje todos tus mensajes de forma sincrónica cuando estés en el entorno dev
.
Echa un vistazo a config/packages/messenger.yaml
. Una de las partes comentadas de este archivo es una especie de transporte "sugerido" llamado sync
. La parte realmente importante no es el nombre sync
sino el DSN: sync://
. Ya hemos aprendido que Messenger admite varios tipos de transporte, como Doctrine, redis y AMQP. Y la forma de elegir cuál quieres es el comienzo de la cadena de conexión, como doctrine://
. El transporte sync
es realmente ingenioso: en lugar de enviar realmente cada mensaje a una cola externa... simplemente los maneja inmediatamente. Se manejan de forma sincrónica.
Podemos aprovechar esto y utilizar un truco de configuración para cambiar nuestros transportesasync
y async_priority_high
para utilizar el transporte sync://
sólo cuando estemos en el entorno dev
.
Entra en el directorio config/packages/dev
. Los archivos que se encuentran aquí sólo se cargan en el entorno dev
y anulan todos los valores del directorio principal config/packages
. Crea un nuevo archivo llamado messenger.yaml
... aunque el nombre de este archivo no es importante. Dentro, pondremos la misma configuración que tenemos en nuestro archivo principal: framework
messenger
, transports
. Luego anula async
y ponle sync://
. Haz lo mismo con async_priority_high
: ponle sync://
.
framework: | |
messenger: | |
transports: | |
async: 'sync://' | |
async_priority_high: 'sync://' |
Eso es todo En el entorno de desarrollo, estos valores anularán los valores de dsn
del archivo principal. Y, podemos ver esto: en una pestaña abierta del terminal, ejecuta:
php bin/console debug:config framework messenger
Este comando te muestra la configuración real y definitiva en framework
y messenger
. Y... ¡sí! Como actualmente estamos en el entorno dev
, ambos transportes tienen un dsn
establecido en sync://
.
Quiero mencionar que la opción queue_name
es algo específico de Doctrine. El transporte sync
no la utiliza y, por tanto, la ignora. Es posible que en una futura versión de Symfony, esto arroje un error porque estamos utilizando una opción no definida para este transporte. Si eso ocurre, sólo tendríamos que cambiar el formato YAML para establecer la clave dsn
-como hacemos en el archivo principalmessenger.yaml
- y luego anular la clave options
y establecerla en una matriz vacía. Lo menciono por si acaso.
Bien, ¡probemos esto! Actualiza la página para estar seguro. Ah, y antes de subir algo, vuelve al terminal donde se está ejecutando nuestro trabajador, pulsa Control+C para detenerlo y reinícialo. ¡Woh! ¡Se ha estropeado!
No puedes recibir mensajes del transporte de sincronización.
Messenger está diciendo:
¡Oye! Um... el SyncTransport no es una cola real de la que puedas leer... así que ¡deja de intentar hacerlo!
Es cierto... y esto es exactamente lo que queríamos: queríamos poder hacer llamar a nuestros manejadores en el entorno dev
sin tener que preocuparnos de ejecutar este comando.
Bien, ahora vamos a probarlo: sube un par de fotos y... sí... vuelve a ser súper lento. Pero Ponka se añade cuando termina. Los mensajes se están gestionando de forma sincrónica.
Para asegurarte de que esto sólo ocurre en el entorno dev
, abre el archivo.env
y cambia APP_ENV
por prod
temporalmente. Asegúrate de borrar la caché para que esto funcione:
php bin/console cache:clear
Ahora, deberíamos poder ejecutar messenger:consume
como antes:
php bin/console messenger:consume -vv async_priority_high async
Y... ¡podemos! Sincroniza los mensajes en dev, async en prod.
Ahora que hemos conseguido esto, vuelve a cambiar APP_ENV
por dev
y, para que las cosas sean más interesantes para el tutorial, comenta la nueva configuración sync
que acabamos de añadir: Quiero seguir utilizando nuestros transportes reales mientras codificamos. Detén y reinicia el trabajador:
framework: | |
messenger: | |
# transports: | |
# async: 'sync://' | |
# async_priority_high: 'sync://' |
Ahora que estamos de vuelta en el entorno dev
, para y reinicia el trabajador:
php bin/console messenger:consume -vv async_priority_high async
A continuación: vamos a hablar de un problema similar: ¿cómo se manejan los transportes al escribir pruebas automatizadas?
Hey Matias C.
There is a way to configure your handler so they can decide which transport to use. Check out this chapter https://symfonycasts.com/screencast/messenger/from-transport
but based on your situation I'd just execute the handler directly in my cronjob command e.g.
// SomeCommand.php
public function __construct(SomeHandler $someHandler)
{
$this->someHandler = $someHandler;
}
protected function execute(Input $input, Output $output)
{
// execute the __invoke method of this handler
($this->someHandler)(
new SomeMessage($argument);
);
}
Cheers!
Hi @MolloKhan thanks for the answer.
Cool thing calling the handler directly, I hadn't thought of that.
On the other hand, the chapter you sent me it's not about choosing transport, but choosing handlers (right?). The handler decides wether to execute or not depending on the transport the message has been sent to.
I'm still confused about how to programmatically route a message to one or another transport. I was browsing the code and there's a HandlersLocatorInterface that looks promising, but I can't find any documentation about it. I can see that there's a compiler pass that configures a HandlersLocator with the yaml configuration, but nothing more.
It would be cool to use a stamp to define which handler to use.. does it make any sense?
Cheers!
Hey Matias C.
Yes, you're right, that's only for choosing which transport to use. I was looking for a solution to your problem and couldn't find anything buit-in. What you can do is to create your own stamp and transport. In the sender you will check if the envelope has such stamp and then decide which transport to use
Here you can read how to create a custom Transport https://symfony.com/doc/cur...
Cheers!
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // v1.8.0
"doctrine/doctrine-bundle": "^1.6.10", // 1.11.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.6.3
"intervention/image": "^2.4", // 2.4.2
"league/flysystem-bundle": "^1.0", // 1.1.0
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.1
"sensio/framework-extra-bundle": "^5.3", // v5.3.1
"symfony/console": "4.3.*", // v4.3.2
"symfony/dotenv": "4.3.*", // v4.3.2
"symfony/flex": "^1.9", // v1.18.7
"symfony/framework-bundle": "4.3.*", // v4.3.2
"symfony/messenger": "4.3.*", // v4.3.4
"symfony/property-access": "4.3.*", // v4.3.2
"symfony/property-info": "4.3.*", // v4.3.2
"symfony/serializer": "4.3.*", // v4.3.2
"symfony/validator": "4.3.*", // v4.3.2
"symfony/webpack-encore-bundle": "^1.5", // v1.6.2
"symfony/yaml": "4.3.*" // v4.3.2
},
"require-dev": {
"easycorp/easy-log-handler": "^1.0.7", // v1.0.7
"symfony/debug-bundle": "4.3.*", // v4.3.2
"symfony/maker-bundle": "^1.0", // v1.12.0
"symfony/monolog-bundle": "^3.0", // v3.4.0
"symfony/stopwatch": "4.3.*", // v4.3.2
"symfony/twig-bundle": "4.3.*", // v4.3.2
"symfony/var-dumper": "4.3.*", // v4.3.2
"symfony/web-profiler-bundle": "4.3.*" // v4.3.2
}
}
Hi there , thanks for this series, it's very good.
I have a question dough.. What do you think would be the correct approach to dynamically change if a message should be handled in the sync or async transport? I have a message that can be dispatched in a cronjob or in a controller. In the cronjob I want it to be handled sync and in the controller async.
Thanks a lot!