Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Intercambio de Claves de Enrutamiento y Vinculación

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

Vamos a cambiar este retraso a un segundo... para no esperar todo el día a que se procesen nuestras fotos.

... lines 1 - 23
class ImagePostController extends AbstractController
{
... lines 26 - 40
public function create(Request $request, ValidatorInterface $validator, PhotoFileManager $photoManager, EntityManagerInterface $entityManager, MessageBusInterface $messageBus)
{
... lines 43 - 63
$envelope = new Envelope($message, [
new DelayStamp(1000)
]);
... lines 67 - 69
}
... lines 71 - 98
}

Configuración sencilla: 1 Intercambio Fanout por cola

En messenger.yaml, los mensajes enviados a cada transporte - async yasync_priority_high - deben entregarse finalmente en dos colas diferentes para que podamos consumirlos de forma independiente. Y... ¡lo hemos conseguido!

Pero hay dos formas diferentes en las que podríamos haber hecho esto. En primer lugar, recuerda que en AMQP los mensajes se envían a un intercambio, no a una cola. Ahora mismo, cuando un mensaje se dirige al transporte async, Messenger lo envía a un intercambio llamado messages. No ves esa configuración aquí sólo porque messages es el nombre de intercambio por defecto en Messenger.

Cuando un mensaje se dirige al transporte async_priority_high, Messenger lo envía a un intercambio llamado messages_high_priority. Cada transporte siempre envía a exactamente un intercambio.

Entonces, cada intercambio enruta cada mensaje a una sola cola, como el intercambio messagesenvía a una cola messages... y messages_high_priority envía a una cola messages_high. No hay una clave de enrutamiento en la vinculación: Messenger vincula cada intercambio a una cola... pero sin clave de enrutamiento. Así es como funciona un intercambio "fanout": no le importan las claves de enrutamiento... simplemente envía cada mensaje a cada cola vinculada a él.

1 Intercambio directo a 2 colas

Ésa es una forma de resolver el problema. La otra forma consiste en tener un único intercambio... pero haciéndolo lo suficientemente inteligente como para enviar algunos mensajes a la cola messages y otros mensajes a messages_high. Lo hacemos con claves de enlace y enrutamiento más inteligentes... que ya vimos con el intercambio delays.

Configurar un intercambio directo

Vamos a refactorizar nuestros transportes para utilizar este sistema "más inteligente". En el transporte async, añade options, luego exchange, y establece name en messages. Si nos detuviéramos aquí, esto no cambiaría nada: este es el nombre de intercambio por defecto en Messenger.

framework:
messenger:
... lines 3 - 19
transports:
... line 21
async:
... lines 23 - 25
options:
... line 27
exchange:
name: messages
... lines 30 - 53

Pero ahora, añade una clave type configurada como direct. Esto sí cambia las cosas: el valor por defecto es fanout. Añade una clave más debajo de esta default_publish_routing_key configurada en normal.

framework:
messenger:
... lines 3 - 19
transports:
... line 21
async:
... lines 23 - 25
options:
... line 27
exchange:
name: messages
type: direct
default_publish_routing_key: normal
... lines 32 - 53

Hablaré de ello en un segundo. A continuación, añade una sección queues. Vamos a "vincular" este intercambio a una cola llamada messages_normal. Pero no nos detendremos ahí Debajo de esto, añade binding_keys ajustado a [normal].

framework:
messenger:
... lines 3 - 19
transports:
... line 21
async:
... lines 23 - 25
options:
... line 27
exchange:
name: messages
type: direct
default_publish_routing_key: normal
... lines 32 - 33
queues:
messages_normal:
binding_keys: [normal]
... lines 37 - 53

Esa palabra normal podría ser cualquier cadena. Pero no es casualidad que coincida con lo que hemos establecido para default_publish_routing_key.

Borrar todos los intercambios y colas

En lugar de hablar mucho sobre lo que hará esto... ¡vamos a verlo en acción! Haz clic para eliminar una foto: eso debería enviar un mensaje al transporte async. ¡Pero la llamada AJAX explota! Abre el perfilador para ver el error. Ah:

Error del canal del servidor: 406, mensaje: PRECONDITION_FAILED - inequivalent arg 'tipo' para el intercambio 'mensajes': recibido 'directo' pero el actual es 'fanout'

El problema es que ya tenemos un intercambio llamado messages , que es de tipo fanout... pero ahora estamos intentando utilizarlo como un intercambio direct. ¡AMQP nos avisa de que estamos intentando hacer una locura!

Así que vamos a empezar de nuevo. Ahora que estamos haciendo las cosas de una manera nueva, vamos a pulsar el botón de reinicio y permitir que Messenger cree todo lo nuevo.

Busca tu terminal - yo cerraré la sesión de MySQL - y detén tu trabajador... de lo contrario seguirá intentando crear tus intercambios y colas con la antigua configuración.

Luego vuelve al administrador de RabbitMQ, borra el intercambio messages... y luego el intercambio messages_high_priority. Y aunque las colas no se verán diferentes, para estar más seguros, borremos también las dos.

Así volvemos a no tener colas y a tener sólo los intercambios originales que creó AMQP -que de todas formas no estamos utilizando- y el intercambio delays. ¡Estamos empezando de cero!

Volvamos a nuestro sitio, borremos la segunda imagen y... ¡parece que ha funcionado! Genial! ¡Veamos qué ha pasado dentro de RabbitMQ! ¡Sí! Tenemos un nuevo intercambio llamadomessages y es de tipo directo. Dentro, tiene un único enlace que dice

Cuando se envía un mensaje a este intercambio con una clave de enrutamiento llamada normal, se entregará a la cola llamada messages_normal.

Todo esto se ha establecido gracias a la configuración de queues y binding_keys. Esto le dice a Messenger:

Quiero que cree una cola llamada messages_normal. Además, asegúrate de que que hay un enlace en el intercambio que dirigirá cualquier mensaje con una clave de enrutamiento establecida en normal a esta cola.

Pero... ¿envió Messenger el mensaje con esa clave de enrutamiento? Hasta ahora, aparte de las cosas de retraso, Messenger ha estado entregando nuestros mensajes a AMQP sin clave de enrutamiento. La configuración de default_publish_routing_key cambia eso. Dice:

Cada vez que un mensaje se enruta al transporte async, quiero que lo envíe al intercambio messages con una clave de enrutamiento establecida en normal.

Todo esto significa que si miramos las colas... ¡sí! ¡Tenemos una cola message_normalcon un mensaje esperando dentro! ¡Lo hemos conseguido!

A continuación, repitamos esto para el otro transporte. Entonces, aprenderemos cómo esto nos da la flexibilidad de controlar dinámicamente dónde se entregará un mensaje en el momento en que lo enviemos.

Leave a comment!

8
Login or Register to join the conversation

fanot vs direct exchange ?

Reply

Hey Jeremy,

Could you clarify your message adding a bit more context? :)

Cheers!

Reply
Default user avatar
Default user avatar Olya Ladygina | posted hace 3 años

How I can add binding_keys for queue when application is already running (not in config)?

Reply

Hi Olya Ladygina!

The binding_keys is only used by Symfony when it's "setting up" the queues. So basically, if you already have a queue, there is nothing stopping you from manually going into RabbitMQ and adding the binding key there. Symfony won't know or care that this happened :). I would also add it to the config, just for consistency (unless you're auto_setup: false, which means you're telling Symfony to never do any setup - in that case, binding_keys is truly 100% ignored and unnecessary).

Let me know if that makes sense!

Cheers!

Reply
Default user avatar
Default user avatar Olya Ladygina | weaverryan | posted hace 3 años

Thanks for the answer, but unfortunately this does not suit me, because I cannot know what binding_keys I need to add. And there is no point in manually adding or removing them, the application should do it.

Reply

Hi Olya Ladygina!

Ah, ok! Can you explain what you need to accomplish a bit more? I think I am misunderstanding, and not giving you a good answer :). What is the actual problem that you need to solve? That will help me see why you can't put the binding_keys into config.

Cheers!

Reply
Roman A. Avatar
Roman A. Avatar Roman A. | posted hace 3 años

Do you think that it is a really good way to initialize all settings kinda "side-effect" style? I think it is the wrong approach for a production server. Instead using the auto_setup option in the messenger component we have to configure all the things manually. It would be cool if you explain how to configure it by yourself in the Rabbit. Or at least just mention that it is totally not a good idea to use the auto_setup option in production.

Reply

Hey Roman,

Could you want this tutorial up to the 44 chapter, including 44? Here's where exactly we set auto_setup to false: https://symfonycasts.com/sc... . I suppose you will answer the question yourself after it. Or ping us again if it would be still fuzzy to you.

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