Chapters
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Hasta ahora, hemos separado las instrucciones de lo que queremos hacer -queremos añadir Ponka a este ImagePost
- de la lógica que realmente hace ese trabajo. Y... es un buen patrón de codificación: es fácil de probar y si necesitamos añadir Ponka a una imagen desde cualquier otra parte de nuestro sistema, será súper agradable.
Pero este patrón desbloquea algunas posibilidades importantes. Piénsalo: ahora que hemos aislado las instrucciones sobre lo que queremos hacer, en lugar de manejar el objeto comando inmediatamente, ¿no podríamos, en teoría, "guardar" ese objeto en algún sitio... y leerlo y procesarlo después? Así es... básicamente como funciona un sistema de colas. La ventaja es que, dependiendo de tu configuración, podrías poner menos carga en tu servidor web y dar a los usuarios una experiencia más rápida. Por ejemplo, ahora mismo, cuando un usuario hace clic para subir un archivo, tarda unos segundos antes de que finalmente aparezca aquí. No es el mayor problema, pero no es lo ideal. Si podemos arreglarlo fácilmente, ¿por qué no?
Hola transportes
En Messenger, la clave para "guardar el trabajo para más tarde" es un sistema llamado transportes. Abre config/packages/messenger.yaml
. ¿Ves esa tecla transports
? En realidad, los detalles se configuran en .env
.
La idea es la siguiente: vamos a decirle a Messenger:
¡Oye! Cuando cree un objeto
AddPonkaToImage
, en lugar de manejarlo inmediatamente, quiero que lo envíes a otro lugar.
Ese "otro lugar" es un transporte. Y un transporte suele ser una "cola". Si eres nuevo en esto de las colas, la idea es refrescantemente sencilla. Una cola es un sistema externo que "retiene" información en una gran lista. En nuestro caso, retendrá los objetos de mensaje serializados. Cuando le enviamos otro mensaje, lo añade a la lista. Más tarde, puede leer esos mensajes de la cola uno a uno, manejarlos y, cuando haya terminado, la cola lo eliminará de la lista.
Claro... los sistemas de cola robustos tienen un montón de otras campanas y silbatos... pero ése es realmente el concepto principal.
Tipos de transporte
Hay un montón de sistemas de colas disponibles, como RabbitMQ, Amazon SQS, Kafka y colas en el supermercado. Fuera de la caja, Messenger soporta tres: amqp
-que básicamente significa RabbitMQ, pero técnicamente significa cualquier sistema que implemente la especificación "AMQP"- doctrine
y redis
. AMQP es el más potente... pero a menos que ya seas un profesional de las colas y quieras hacer alguna locura, todos ellos funcionan exactamente igual.
Ah, y si necesitas hablar con algún transporte no soportado, Messenger se integra con otra biblioteca llamada Enqueue, que soporta un montón más.
Activar el transporte de la doctrina
Como ya estoy utilizando Doctrine en este proyecto, vamos a utilizar el transporte doctrine
. Descomenta la variable de entorno para ello
Show Lines
|
// ... lines 1 - 29 |
###> symfony/messenger ### | |
Show Lines
|
// ... lines 31 - 32 |
MESSENGER_TRANSPORT_DSN=doctrine://default | |
Show Lines
|
// ... line 34 |
###< symfony/messenger ### |
¿Ves esta parte de ://default
? Eso le dice al transporte Doctrine que queremos utilizar la conexión default
Doctrine. Sí, reutilizará la conexión que ya has configurado en tu aplicación para almacenar el mensaje dentro de una nueva tabla. Pronto hablaremos de ello.
Tip
A partir de symfony 5.1, el código del transporte Doctrine se trasladó a su propio paquete. La única diferencia es que ahora también debes ejecutar este comando:
composer require symfony/doctrine-messenger
Ahora, de vuelta en messenger.yaml
, descomenta este transporte async
, que utiliza la variable de entornoMESSENGER_TRANSPORT_DSN
que acabamos de crear. El nombre - async
- no es importante - podría ser cualquier cosa. Pero, en un segundo, empezaremos a hacer referencia a ese nombre.
framework: | |
messenger: | |
Show Lines
|
// ... lines 3 - 5 |
transports: | |
# https://symfony.com/doc/current/messenger.html#transports | |
async: '%env(MESSENGER_TRANSPORT_DSN)%' | |
Show Lines
|
// ... lines 9 - 16 |
Enrutamiento a los transportes
Llegados a este punto... ¡vaya! Le hemos dicho a Messenger que tenemos un transporte async
. Y si quisiéramos volver y subir un archivo ahora, no habría... ninguna diferencia: se seguiría procesando inmediatamente. ¿Por qué?
Porque tenemos que decirle a Messenger que este mensaje debe ser enviado a ese transporte, en lugar de ser tratado ahora mismo.
Volviendo a messenger.yaml
, ¿ves esta clave routing
? Cuando enviamos un mensaje, Messenger mira todas las clases de esta lista... que ahora mismo es cero si no cuentas el comentario... y busca nuestra clase - AddPonkaToImage
. Si no encuentra la clase, maneja el mensaje inmediatamente.
Digámosle a Messenger que, en su lugar, lo envíe al transporte async
. EstableceApp\Message\AddPonkaToImage
en async
.
framework: | |
messenger: | |
Show Lines
|
// ... lines 3 - 12 |
routing: | |
# Route your messages to the transports | |
'App\Message\AddPonkaToImage': async |
En cuanto lo hagamos, habrá una gran diferencia. Observa lo rápido que se carga la imagen a la derecha después de cargarla. ¡Bum! Ha sido más rápido que antes y... ¡Ponka no está ahí! ¡Jadea!
En realidad, vamos a probar una más - esa primera imagen era un poco lenta porque Symfony estaba reconstruyendo su caché. Esta debería ser casi instantánea. ¡Lo es! En lugar de llamar a nuestro manejador inmediatamente, Messenger está enviando nuestro mensaje al transporte Doctrine.
Viendo el mensaje en cola
Y... um... ¿qué significa eso en realidad? Busca tu terminal... o cualquier herramienta que te guste utilizar para jugar con las bases de datos. Yo utilizaré el cliente mysql
para conectarme a la base de datos messenger_tutorial
. Dentro, vamos:
SHOW TABLES;
¡Woh! Esperábamos migration_versions
y image_post
... pero de repente tenemos una tercera tabla llamada messenger_messages
. Veamos qué hay ahí:
SELECT * FROM messenger_messages;
¡Bien! ¡Tiene dos filas para nuestros dos mensajes! Utilicemos la magia \G
para darle un formato más bonito:
SELECT * FROM messenger_messages \G
¡Genial! El body
contiene nuestro objeto: ha sido serializado utilizando la funciónserialize()
de PHP... aunque eso puede configurarse. El objeto está envuelto dentro de algo llamado Envelope
... pero dentro... podemos ver nuestro objetoAddPonkaToImage
y el ImagePost
dentro de éste... completo con el nombre del archivo, la fecha createdAt
, etc.
Espera... ¿pero de dónde viene esta tabla? Por defecto, si no está ahí, Messenger la crea por ti. Si no quieres eso, hay una opción de configuración llamadaauto_setup
para desactivar esto - más adelante te mostraré cómo. Si desactivaste la configuración automática, podrías utilizar el práctico comando setup-transports
en el despliegue para crear esa tabla por ti.
php bin/console messenger:setup-transports
Esto no hace nada ahora... porque la tabla ya está ahí.
¡Este fue un gran paso! Cada vez que subimos imágenes... no se gestionan inmediatamente: cuando subimos dos más... se envían a Doctrine y éste hace un seguimiento de ellas. ¡Gracias Doctrine!
A continuación, es el momento de leer esos mensajes uno a uno y empezar a manejarlos. Lo hacemos con un comando de consola llamado "trabajador".
37 Comments
Yo Tomáš S.!
Sorry for the slow reply! There's no built-in mechanism inside of Messenger to do this specifically... in part because that's not a normal function of a queueing system. So, you have a few options for this:
A) Delay the message precisely. We know that we can DELAY a message. And so, you could definitely calculate that "tomorrow at 8pm" is, for example, 20 hours away... and then apply a DelayStamp for that amount of time. I can't see any problem with that, other than you need to do the math to figure out how long to do the delay ;).
B) Your other option is to work outside of Messenger. For example, save some record in the database (or some flag) that this messages needs to be sent. Then run a CRON job (e.g. every 30 minutes or 5 minutes... depending on the need) that is constantly looking for "messages that are ready to be sent". We do this kind of thing, for example, to send "your subscription will renew soon" reminders to annual subscribers. This is a case where we can't use Messenger, because it would involve sending the message when the user first subscribes... but delaying it for around 360 days... and also we would need to *remove* that message if the user cancelled. Obviously, not a clean use-case for Messenger :).
Let me know if this helps!
Cheers!
Hi,
for some reason the database table messenger_messages does not get created automatically. I tried generated the statements with make:migration but to no avail. I'm using Symfony 4.4. Any idea what I'm doing wrong?
EDIT: I can execute the command bin/console messenger:setup-transports
that will create the table :)
Hey Dirk
I'm glad to hear that you could find the solution to your problem. If I recall correctly, Messenger should create that table by default if you have the auto_setup
option enabled
Cheers!
Hi, thanks for this tutorial!
Doctrine transport is good no need to install more dependencies.
But I'm wondering about one thing message under my logs, I think each 1 minute (not sure):
SELECT m.* FROM messenger_messages m WHERE (m.delivered_at is null OR
m.delivered_at < ?) AND (m.available_at <= ?) AND (m.queue_name = ?) ORDER BY available_at ASC LIMIT 1 FOR UPDATE ["2020-09-15 14:04:21","2020-09-15 15:04:21","default"] []
So each 1 minute I'm calling the database to fetch messages.
It does not overload the BDD this kind of practice. How we can override that 1 minute ? And is there a good practice about the hole stroy !!
- With rabbitmq transport, do we send requests exactly the same to rabbit mq for message fetching!
Thank you.
Hey ahmedbhs
I believe you're looking for the --sleep
option of the messenger:consume
command, so you can tell your worker to wait X seconds before fetching new messages after not finding any.
It does not overload the BDD this kind of practice. How we can override that 1 minute ? And is there a good practice about the hole stroy !!
Usually in an integration test, what you test for is if the message was sent or not. And, in an unit test you test the handler's behavior
Cheers!
Helo guys i'm new to symfony cast, thank you for the amazing tuto:
1/ By what criteria Symfony messenger knows that this is the right time to process the following message. How the broker knows that there is some memory available and that the handler has finished processing all these messages to go to the next message! How ke broker knows that the handler has finished his job. I'm afraid that in some case it can lead to memory problems.
2/ Is it good way to decouple code via messenger or event ubscriber is the traitment is synchrone?
3/ What is the best way to send 1000 emails per a day.
4/ Is there some usescase where it's not necessary to use messenger ! I still dont understand why why use symfony messenger !
5/ For example, do we use the messenger when we are treating 3 dependent tasks to each other or not ?
6/ Is queing système is synchronous traitment or asynchronous ? I mean queing is also synchronous he trait thing in FIFO mode. It's clear that the user gonna not wait for the response and that help for user expirience.
Hey Taken,
Wow, a lot of questions at once... have you watched this course up to the end? Because I suppose you will get answers on almost all your questions in it later :) OK, a few thoughts about your questions from me:
1. To handle messages you have to run "bin/console messenger:consume-messages" command, where you can specify memory limit with "--memory-limit" option. And this command tracks memory usage, so when the command handles a new message - it will check if the memory limit you specified is reached. As soon as it found the memory limit was hit - it will exit. That's why you need to specify a memory limit lower than your max memory. If you set 128MB memory limit - command will be stopped only when the memory is over this limit. E.g. used memory after 8th message is 121MB, 121 < 128, so the command will start handling a new message, but e.g. used memory after 9th message is 136MB, 136 > 128, so the command will exit. As you see, if you max allowed memory size would be 128 - the 9th message will fail because of free no memory. Usually --memory-limit is 2x lower than max memory limit to avoid "max memory reached" errors.
2. It depends, by following this course you would see when it's better to use messenger. In some cases, your code might become more clean with messenger even if you use it sync, but sometimes it just may complicate things and lead to bugs if you will do it incorrectly. The advantage is that if you don't need the async handling yet, but switched to messenger architecture using sync, you would be easier to switch your project to async message handling later if needed - it's just like change one line of code in your configuration to use a new transport.
3. It depends on your use cases :) If you have 1000 new users every day, and all them you sends welcome emails after registration - that's totally ok to do it sync, like after each successful registration send the email immediately. But if you need at some point send 1000 emails to your subscribers - that's another question, and that's why messenger really may help.
4. Haha, please, watch the course till the end, I bet you will answer this question yourself ;) But basically, maybe you even don't need it at all. You don't have use ALL Symfony components in your project :) Usually, people use messenger to do some heavy operations async, that's the best benefit of using messenger.
5. Once again, it depends. If those tasks are heavy - you may think of doing them async to return your users response faster.
6. If we're talking about messenger component - it depends on which transport you use. If you put some tasks to your queue but use sync transport - messenger will handle the message in the same process, so your users will wait the response until messenger finished processing the message. For example, after user registration you send a welcome email. If you will do it with messenger but with sync transport. User will see your "You're successfully registered" message only when the email is actually been sent. But if you will do it async - messenger will just add that message to the queue and you will show the user "You're successfully registered" message faster, even before the message is actually been sent to them. And another process, that handles those messages will eventually handles that message later and actually sent the email. So as a result, we may consider the 2nd option with async as better user experience as you return the response a little faster to user and then your system handles the remaining work.
I hope this is a bit clearer for you. But I'd really suggest you to watch the course completely to understand the Messenger better.
Cheers!
Hi Rayen thank you so mush for the tuto,
* There's some point of view that saying "a database is not a message queue." is this is true. I'm sure that, a database makes it very easy to centralize information, and we know very well how to save and redundate it.
A lot of online recommendation say that we should not use a database as a message queue because problems can be appeared during concurrency, for exemple: if we made a SELECT request to retrieve the identifier of the next task to be processed, then an UPDATE request to indicate that this task is being treatment ; obviously, if two programs do this at the same time, the task may be processed twice.
* The fact remains that it requires polling data Fromm database for each task, that is to say, scanning the database very regularly to see if there are new tasks waiting. It is true that this is not ideal, because it unnecessarily loads the base, but above all implies latency in the treatments
I know that doctrine transport is amazing for development experience. But I'm wondring is symfony core team already solved the's problems.
PS: This is the slide that talk about we should not use database as a queue système https://fr.slideshare.net/R...
Thank you!
Hey ahmedbhs
As far as I know Symfony Messenger covers the concurrency problems when using Doctrine (the database) as a message transport. Actually, we use it on Symfonycasts, we have a couple of workers to consume all the messages and we haven't got any issues related to consume the same message twice
Cheers!
Hi! Is there a way to save all the messages even if they were consumed successfully? It may be quite useful. For example, I want to store history about all messages.
This moment I can guess that I can create my own Transport, but maybe there is a more elegant and simple way?
Hey Roman,
Interesting question :) Hm, creating a new transport and duplicate messages to it does not mean the messages were successfully handled in other transports, so most probably it's not something you need. Actually, the everything should be logged, so maybe it's enough for you to have those logs? I think you should be able to hook the Messenger, it has some events, you can take a look at them here: https://github.com/symfony/... . Or, as an alternative solution you can create your own middleware and then do whatever you want in it, like storing successfully handled messages in a separate table, etc.
I hope this helps!
Cheers!
Hello ! Thanks for this tuto (amazing work!),
I faced "Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed", trying to process upload files but without saving them on hosting? The goal is to merge all of this stuff asynchronously, and avoid waiting time for users, but I really want to avoid saving...
Actually my handler works perfectly while messenger is synchronous but find the error on async. Could you help me? <3
Yo @Rémi!
Ah, really cool question. I totally get what you're doing - you want to process the file upload later so the user doesn't have to wait. Hmm, here's how I would do this, at least approximately - and you can let me know what you think ;).
1) Move the temporary-uploaded file somewhere that the worker will have access to. If you have just one server, this is easy: just move it somewhere on your filesystem that is not the temporary directory (because I believe PHP deletes the tmp uploaded file at the end of the request - so that's no good!).
2) Create a new message object that is, sort of, similar to the UploadedFile. It would contain anything you find relevant: like the original filename, the path to where you moved the file on the filesystem... and anything else. This will absolutely be serializable, because it will contain very simple data.
Another option it to put the binary contents of the file itself onto the message class. However, I think you may run into problems if you do this with the Doctrine transport (which, out-of-the-box doesn't like binary content, though you could base64_encode it to get around this). Also, this might slow things down even worse (not sure, just guessing), because now you would need to send the (for example) 2mb of data to your queue and your user would need to wait for this to happen.
Let me know what you think!
Cheers!
Good options ! And I had tested two of them yet... ;)
1) I don't really want to save temporary-uploaded files (whooo,so scary, am I working on secrets stuffs? shhhh!) but i guess I will resignate because as you said PHP delete them at the end of the request. Life is sad but I know this is the right thing to do.
2) Yes, doing this already, it's fine.
3) Mmm agree, on Doctrine transport it'll definitively take too long and i'm working on heavy stuff.
You gave me to think, thank you so much (and I have an other question for you, asking it in the right tuto)
Hi, thanks for this tutorial.
I have created some messages and I used doctrine transport like in this tutorial , but I want to ask you as you are an expert in symfony , better using doctrine transport or rabbitmq ? which one is the most efficient and faster ? Thanks
Hey hous04!
Cool question :). Short answer: both are fine. Slightly-less short answer: RabbitMQ is better, faster and more powerful.
Here's an analogy. Suppose you want to cache something. Should you use "filesystem" cache or Redis? For many situations, both are just fine. If you have a huge, heavy-traffic, distributed app, Redis will start to make a difference. Or, if you use some advanced functionality or Redis itself (beyond just using it as a key-value store for data), then you'll need Redis.
The exact same is true for RabbitMQ. It's definitely superior to Doctrine. But, unless you are processing many, many messages, I don't think you would notice much performance difference. Using the Doctrine transport will result in frequent (but fast) queries made to fetch messages... but that's about it. RabbitMQ also has many more advanced features & workflows... which if you don't know about them, then you don't need them... at least not yet ;).
So, stick with Doctrine unless you have the need for more complex use-case, already know Rabbit or are excited to learn and try it.
Let me know if this clears things up!
Cheers!
It's very clear thank you. I'll test both ;)
Hi! Right now when I type bin/console messenger, I only see debug:messenger and messenger:consume-messages, so I'm missing the rest of commands, any idea why? Thanks!
Hi Again! I was following the videos using my own project, which it was on 4.2.7 symfony version...So, upgrading to 4.3 (as the downloaded code) did the trick. Cheers!
Hey Alex,
Glad you found the problem yourself and was able to fix it! Yeah, each new version might bring new commands, it's a good idea to check your version first if you don't see some of them in the list.
And thank you for your feedback! It might be useful for others ;)
Cheers!
I used this tutorial to build a queueing system about two years ago and it worked OK in the beginning. The system receives inputs from several different sources and a lot of the messages are duplicates. As we grow, the duplicate messages are causing problems because our server communicates with another system via an API and they limit our service if the the number of duplicate requests exceeds a certain threshold. The more duplicates we receive, the longer the queue gets and the longer it takes to process them all.
I'm trying to figure out how to detect duplicate messages and avoid dispatching them. Right now I've developed a hack that runs from cron once every minute and deletes the duplicates from the database. However, this is just a workaround only works if I'm using the database to store messages. I'd like create a generic solution that would work with other transports like RabbitMQ or Redis.
I've been toying with idea of using the `DelayStamp` to force the messages to be stored in queue (say for 30 seconds) before anything happens. Then I could create a "deduplication" process that would continually run in the background and check the queue for duplicate messages (like every 10 seconds). This basically what my SQL script hack is doing right now, but I'd like to create a generic solution that would work with other transports.
I've started looking through the messenger library code, but am not sure where to start. This might be a feature that could be useful to others and I don't mind contributing to the project.
Any ideas on how to do this?
Hey Jeff,
I probably does not understand the your case completely, but if you get a lot of duplicates - it sounds like you're missing validation. I think you should validate the form that sends the data that might be duplicated. The most widely used Symfony constraint for this is the UniqueEntity, see docs: https://symfony.com/doc/cur... . In theory, if you will reject duplicated data on form submit because of invalid form - you won't add duplicated messages to the queue and so it will handle much limited set of "good" messages.
I hope this helps!
Cheers!
Sorry, but the inputs are not generated by a form. They are triggered by various activities and they need to be buffered and deduplicated before a message is dispatched.
Hey Jeff,
Ah, ok then. But in this case I think you can still apply some validation on the server side before dispatching that message. You don't have to work with forms - you can work with symfony validator directly. Here're some examples from the docs: https://symfony.com/doc/cur...
Otherwise, you may probably want different messages... first message handler will deduplicated the messages, and then the 2nd will process already deduplicated messages further - it's like 2 different events (processes). I hope this makes sense to you for your specific case.
I hope this helps!
Cheers!
I'm familiar with Symfony Validator, but I don't think it's the correct tool for this job.
The SQL to delete the duplicate messages is fairly simple:DELETE t1 FROM messenger_messages t1<br />INNER JOIN messenger_messages t2<br />WHERE t1.id < t2.id<br />AND t1.body = t2.body<br />AND t1.headers = t2.headers<br />AND t1.queue_name = t2.queue_name<br />AND t1.queue_name != 'failed'
What I'm really looking for is a deeper understanding of the Symfony Messenger architecture and how to create a generic dedupe that would work on various transports (not just the database). The deduplication could possibly be done in several different places. I could filter out any duplicates before doing the dispatch, or I could do the normal dispatch with a delay and and then do the deduplication before the event is actually processed. I'm trying to figure out the best place in the code to handle it. It would be nice if I had something like a PRE_EVENT_PROCESS event that could the trigger the dedupe (or message ignore) before the event is actually processed.
It might also be possible to create an IgnoreDuplicateStamp()
stamp that could handle things. This stamp would cause messenger to first look for an existing copy of the message and then discard the current message if one is found. In order to make a generic solution I need to figure out how to search for messages in Redis, RabbitMQ and other transports. I don't know if there is existing code in Messenger to do this or whether I would need to write it.
Hey Jeff G.
That's an interesting question. My first recommendation is to avoid duplicating messages from your application, just add appropiate checks before dispatching the message.
You can achieve that by leveraging Messenger middlewares (we talk about them later in the tutorial)
If that's not an option because there is more than one system dispatching messages, then, the logic for detecting duplicates has to happen on the message handling side (once the message is received and it's about to be processed). There's a message event that you can hook into Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent
, so you just have to write an event listener and decide if such message should be handled or not
Hope it helps, cheers!
I've been looking into that already. I think that I can trigger things with the SendMessageToTransportEvent
event and use that to call a routine that checks for duplicates in the queue. It's fairly simple to write a query to do this when using the messenger_messages
table, but I haven't figured out how to do it when using Redis.
I'm afraid you'll need a custom implementation for each service (DB, RabbitMq, Redis, etc). You can add a custom stamp to the message so you can know which service dispatched the message
Is there a way to determine the transport from the message? For example, could I call getEnvelope()
and figure out the transport? It appears that most of my messages have the BusNameStamp
, so it looks like I can get the bus name stamp, which I can probably link back to a transport.
Or here's an idea that is even simpler...
Since it's unlikely I'll be using multiple transports at same time, I can just use the MESSENGER_TRANSPORT_DSN
environment variable. That will give me the transport and I can write some custom code to do the duplicate message lookup for each one. If I really want to do things the correct way, I can create a FindDuplicateMessageInterface
class and use that to build classes for each transport I need to support.
An IgnoreDuplicateMessageStamp
could determine the transport from MESSAGE_TRANSPORT_DSN
and then provide a findDuplicate()
function that could be called.
> Is there a way to determine the transport from the message?
I don't think you can tell what transport was used by looking at the message object just out of the box. I think you can add a TransportNameStamp to your messages if you really need that flexibility, but if you're going to use only one transport, then, you can simplify things by assuming the transport.
Cheers!
I'll add an option to my IgnoreDuplicateMessageStamp
class that will let you specify the transport. If not set, it will default to the one specified by MESSAGE_TRANSPORT_DSN
. I think that should cover things in case I decide to use multiple transports.
Sounds good! However, as a personal recommendation I suggest to avoid adding unnecessary flexibility, it's always better to wait for a real use-case than invest early in something that you don't know if you'll ever use.
Yes, that's always a trade-off (have been a programmer for 40+ years). At least my architecture should support it.
Wow, 40 years, congrats that's not easy to say! Keep up the good work
Cheers!
Actually longer, if I count the time before graduating from university. I think I wrote my first program in 1970. Still enjoy it. Working at a startup now with other programmers half my age.
"Houston: no signs of life"
Start the conversation!
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.21.6
"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,
I want to ask how to delay consumption for concrete message at specific date and time. Lets assume the message sends an email and I want to do at tommorrow at 8pm.
Cheers Tomas