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 SubscribeAdemás de la carga útil, un mensaje en RabbitMQ también puede tener "cabeceras". Comprueba esa clave en nuestro mensaje. ¡Woh! ¡Contiene una gran estructura JSON con el nombre de la clase original y los datos y nombres de clase de los sellos adjuntos al mensaje!
¿Por qué ha hecho esto Messenger? Bueno, busca tu terminal y consume el transporte async
:
php bin/console messenger:consume -vv async
Esto sigue funcionando. Internamente, el serializador de Symfony utiliza la información deheaders
para averiguar cómo tomar esta simple cadena JSON y convertirla en el objeto correcto. Utilizó la cabecera type
para saber que el JSON debía convertirse en un objetoImagePostDeletedEvent
y luego hizo un bucle sobre los sellos y convirtió cada uno de ellos en un objeto sello para el sobre.
Lo bueno de utilizar el serializador de Symfony en Messenger es que el payload
es una estructura JSON simple y pura que puede ser consumida por cualquier aplicación en cualquier lenguaje. Contiene algo de información de la clase PHP en las cabeceras, pero otra aplicación puede simplemente ignorar eso. Pero gracias a esas cabeceras, si la misma app envía y consume un mensaje, el serializador Symfony puede seguir utilizándose.
Pero espera... si eso es cierto - si el serializador de Symfony crea mensajes que pueden ser consumidos por sistemas externos o por nuestra misma app - entonces ¿por qué no es el serializador por defecto en Messenger? ¡Una excelente pregunta! La razón es que el serializador de Symfony requiere que tus clases sigan algunas reglas para ser serializadas y des-serializadas correctamente - como que cada propiedad necesita un método setter o un argumento constructor donde el nombre coincida con el nombre de la propiedad. Si tu clase no sigue esas reglas, puedes acabar con una propiedad que está establecida en el objeto original, pero que de repente se convierte en nula cuando se lee del transporte. No es divertido.
En otras palabras, el serializador de PHP es más fácil y fiable cuando todo lo hace la misma aplicación.
De todos modos, si estás utilizando el serializador de Symfony, también hay algunas cosas que se pueden configurar. Busca tu terminal y ejecuta:
php bin/console config:dump framework messenger
Comprueba la clave symfony_serializer
. Aquí es donde configuras el comportamiento del serializador: el formato - json
, xml
o algo más, y elcontext
, que es una matriz de opciones para el serializador.
Por supuesto, también puedes crear un servicio de serializador totalmente personalizado. Y si tienes el flujo de trabajo opuesto al que acabamos de describir -uno en el que tu aplicación consume mensajes que fueron enviados a Rabbit desde algún otro sistema- un serializador personalizado es exactamente lo que necesitas. Hablemos de eso a continuación.
Hey Mokhtar T.!
Hmm, that's interesting. So it sounds like you have a situation where it might be normal to receive invalid messages from external services? This wasn't something I've really thought about before. But, if you do have this, I see your point: 3 invalid messages in a row will cause your worker process to fail so rapidly, that you would probably hit the startretries
limit. Here's what I'd recommend:
A) If you can, try avoiding this. I know, this seems obvious - and there is probably some reason why you have this situation. But in general, knowing that you may routinely receive invalid messages to your queue seems odd to me.
B) If you can't avoid it, you could set startsecs
to 0 to avoid it thinking that the restart was unsuccessful.
C) When you throw an exception during the decoding process, the normal Symfony console error system is invoked. This means that you should be able to listen to this event - https://symfony.com/doc/current/components/console/events.html#the-consoleevents-error-event or https://symfony.com/doc/current/components/console/events.html#the-consoleevents-terminate-event - and... do something... like add a pause (a literal sleep(1) ) so that the process waits long enough before exiting that supervisor knows that it DID start up successfully. A little odd... but I'm not sure if there is anything else you can do from these listeners.
D) If you're writing a custom deserializer, you could, I think, return a dummy Message object that has a handle that... does nothing. In other words, you WOULD always return a valid message object... but if there was a deserialization problem, then it's a dummy message whose handle does nothing.
So... a few "decent" options - I hope this will help.
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
}
}
Hello Ryan
sorry to put this question here but as part of serializing message from and external service you mentioned that in case we have an invalid message we should throw MessageEncodingException and this one will kill the process of our consumer and supervisor will restart it but by default supervisor has 3 retires so if we have more than 3 invalid message the queue will switch to idle status and our consumer won't working how to prevent that ? I'm thinking to increase supervisor retries but it looks anugly solution do you have any othe idea ? thanks in advance