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 SubscribeAhora sabemos que cada mensaje se reintentará 3 veces -lo cual es configurable- y luego, si el manejo sigue fallando, será "rechazado"... lo cual es una palabra de "cola" para: se eliminará del transporte y se perderá para siempre.
Eso es... ¡un fastidio! Nuestro último reintento se produjo 14 segundos después del primero... pero si el gestor falla porque un servidor de terceros está temporalmente fuera de servicio... entonces si ese servidor está fuera de servicio aunque sólo sea durante 30 segundos... ¡el mensaje se perderá para siempre! ¡Sería mejor si pudiéramos reintentarlo una vez que el servidor volviera a funcionar!
La respuesta a esto es... ¡el transporte de fallos!
En primer lugar, voy a descomentar un segundo transporte. En general, puedes tener tantos transportes como quieras. Éste comienza con doctrine://default
. Si miras nuestro archivo .env
... ¡eh! ¡Eso es exactamente lo que nuestra variable de entornoMESSENGER_TRANSPORT_DSN
está configurada! Sí, tanto nuestro transporte async
como el nuevo failed
están utilizando el transporte doctrine
y la conexión de doctrina default
. Pero el segundo también tiene esta pequeña opción ?queue_name=failed
. Oooooooooooo.
framework: | |
messenger: | |
... lines 3 - 5 | |
transports: | |
... lines 7 - 12 | |
failed: 'doctrine://default?queue_name=failed' | |
... lines 14 - 20 |
Vuelve a lo que estés usando para inspeccionar la base de datos y comprueba la tabla de colas:
DESCRIBE messenger_messages;
Ah. Una de las columnas de esta tabla se llama queue_name
. Esta columna nos permite crear varios transportes que almacenan todos los mensajes en la misma tabla. Messenger sabe qué mensajes pertenecen a cada transporte gracias a este valor. Todos los mensajes enviados al transporte failed
tendrán un valor failed
... que puede ser cualquier cosa - y los mensajes enviados al transporte async
utilizarán el valor por defecto... que es default
.
Por cierto, cada transporte tiene una serie de opciones de conexión diferentes y hay dos formas de pasarlas: o bien como parámetros de consulta como éste, o bien mediante un formato expandido en el que pones el dsn
en su propia línea y luego añades una clave options
con lo que necesites debajo.
¿Qué opciones puedes poner aquí? Cada tipo de transporte -como doctrine
o amqp
- tiene su propio conjunto de opciones. Ahora mismo, no están bien documentadas, pero son fáciles de encontrar... si sabes dónde buscar. Por convención, cada tipo de transporte tiene una clase llamada Connection
. Pulsaré Shift+Shift en PhpStorm, buscaré Connection.php
... y buscaré los archivos. ¡Ahí están! Una clase Connection
para Amqp, Doctrine y Redis.
Abre la de Doctrine. Todas estas clases tienen documentación cerca de la parte superior que describe sus opciones, en este caso: queue_name
, table_name
y algunas otras, como auto_setup
. Antes hemos visto que Doctrine creará la tablamessenger_messages
automáticamente si no existe. Si no quieres que eso ocurra, debes poner auto_setup
en false
.
El transporte con más opciones se puede ver en la clase Conexión Amqp. Hablaremos de Amqp más adelante en el tutorial.
De todos modos, ¡volvemos a ello! Ahora tenemos un nuevo transporte llamado failed
... que, a pesar de su nombre, es igual que cualquier otro transporte. Si quisiéramos, podríamos encaminar allí las clases de mensajes y consumirlas, tal y como estamos haciendo con async
.
Pero... el objetivo de este transporte es diferente. Cerca de la parte superior, hay otra clave llamada failure_transport
. Descomenta eso y observa que apunta a nuestro nuevo transporte failed
.
framework: | |
messenger: | |
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. | |
failure_transport: failed | |
... lines 5 - 20 |
¿Qué hace? ¡Veámoslo en acción! Primero, ve a reiniciar nuestro trabajador:
php bin/console messenger:consume -vv
¡Woh! Esta vez, nos pregunta qué "receptor" -que básicamente significa qué "transporte"- queremos consumir. Un trabajador puede leer de uno o varios transportes, algo de lo que hablaremos más adelante con los transportes "priorizados". Vamos a consumir sólo el transporte async
- manejaremos los mensajes del transporte failed
de otra manera. Y en realidad, para facilitarnos la vida, podemos pasar async
como argumento para que no nos pregunte qué transporte utilizar cada vez:
php bin/console messenger:consume -vv async
Ahora... ¡vamos a subir algunas imágenes! Entonces... por aquí... rápidamente, las 4 agotan sus reintentos y acaban siendo rechazadas por el transporte. Hasta ahora, eso significaba que habían desaparecido para siempre. Pero esta vez... eso no ocurrió. Antes de eliminar el mensaje de la cola, dice
El mensaje rechazado
AddPonkaToImage
se enviará al transporte de fallo "fallido"
Y luego... "Enviando mensaje". Por tanto, se ha eliminado del transporte async
, pero sigue existiendo porque se ha enviado al transporte "fallido".
¿Cómo podemos ver qué mensajes han fallado y volver a intentarlo si pensamos que esos fallos eran temporales? Con un par de brillantes y nuevos comandos de consola. Hablemos de ellos a continuación.
Hey Roland W.
Yep you can do it with special exception UnrecoverableMessageHandlingException
Cheers!
As far as I can see the UnrecoverableMessageHandlingException ony prevents retries but not the sending to the failure transport, right?
IIRC it should discard message at all without sending it to failed, hm but why you need retries but not failure?
I would have to throw it in the Message Handler, right? Unfortunatelly I guess I need something that works in an Event Subscriber to be able to call this:
if($event->willRetry() === false) { ... }
Becaus I still want the retries but not the failure transport.
// 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
}
}
Is there a possibility to prevent that certain messages are sent to the failure transport after being rejected?