Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Manejar la sincronización de mensajes mientras se desarrolla

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Me 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.

Hola "sync" Transporte

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.

Hacer que los transportes se sincronicen

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 syncque 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?

Leave a comment!

4
Login or Register to join the conversation
amcastror Avatar
amcastror Avatar amcastror | posted hace 3 años | edited

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!

Reply

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!

Reply
amcastror Avatar

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!

Reply

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!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

Este tutorial está construido con Symfony 4.3, pero funcionará bien en Symfony 4.4 o 5.

What PHP libraries does this tutorial use?

// 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
    }
}
userVoice