Prima di addentrarci e scoprire cosa possiamo fare con una coda e ovviamente dei messaggi, cerchiamo di dare una risposta definitiva al perché “two-way messaging” non abbia senso, e quindi esista solo “one-way messaging”. Quando si parla di code abbiamo due macro tipologie di architetture:
Un ottimo esempio di store & forward, e pure l’unico che a quest’ora di mattina mi viene in mente, è MSMQ. Per mandare un messaggio da A a B:
- A manda un messaggio alla sua coda locale di uscita, la coda locale è sulla stessa macchina su cui vive A
- la coda locale di uscita quando avrà connettività cercherà di mandare il messaggio alla coda di input di B sulla macchina remota
- Se l’invio fallisce il messaggio viene spostato in una dead-letter queue
- Se l’invio non avviene entro il timeout prestabilito, concettualmente è come se avesse fallito, di nuovo dead-letter queue
Nel caso dei broker, ad esempio RabbitMQ, quello che succede è:
- A manda un messaggio al broker chiedendo di consegnare il messaggio a B
- Se non c’è connettività con il broker A scoppia immediatamente
- Il broker, supponendo la configurazione più restrittiva possibile:
- Manda un ACK ad A confermando la ricezione del messaggio
- Informa anche A che la destinazione esiste, quindi la coda di B è effettivamente cosa nota
- A seconda della tipologia di clustering del broker stesso non è detto che la coda di B sia effettivamente raggiungibile
Dal punto di vista dell’infrastruttura, se osservate i dialoghi di cui sopra, come mittenti il meglio che possiate ottenere è: il messaggio che ho inviato è stato consegnato nella coda del destinatario, oppure la consegna è fallita. In nessun modo avete possibilità di sapere se il messaggio sia stato processato con successo o se nel momento in cui è stato processato vi sia stato un errore, perché per l’infrastruttura questa informazione può essere consegnata solo con un altro messaggio, e quindi siamo di nuovo al problema dei due generali. Come abbiamo già detto l’unico modo che avreste è tenere in piedi una transazione distribuita tra tutte le parti coinvolte, ma è un ossimoro, perché introduciamo code proprio perché vogliamo rendere indipendenti mittente e destinatario.
Le implicazioni di questo “piccolo” dettaglio sono importanti, prima su tutte è l’impatto che il non poter avere certezza che una richiesta sia stata processata ha su come viene disegnato un sistema. Ecco quindi spiegato perché quando si parla di sistemi distribuiti si parla di “design for failure”.
Ricordatevi sempre che la prima regola di un sistema distribuito è: non è vero che avete bisogno di un sistema distribuito ;-)