Molti di quelli che si approcciano al fantastico mondo della architetture distribuite basate su messaggi partano da un presupposto che non trova conferma e che tende ad essere fuorviante specialmente in fase di design.
Non esiste 2-way messaging
Spesso e volentieri quando si parla di messaggi si parla di pattern come request/response, request/reply e/o pub/sub. Nello specifico i primi due sono quelli che corrono il maggior rischio di essere fraintesi.
Se osserviamo come funziona un’infrastruttura di messaggistica, broker o code che siano, non esiste alcuna implementazione di request/* ma esiste semplicemente “request”, o più grezzamente esiste semplicemente la possibilità di inviare un messaggio. Se proprio vogliamo essere ancora più precisi non esiste neanche questo, piuttosto quello che le infrastrutture di messaggistica vi offrono è:
- Accetta questo messaggio il cui destinatario è XYZ, e, se lo supporti, dammi conferma di averlo accettato (non consegnato, solo accettato).
- Cortesemente, garantiscimi che se non riuscissi a consegnare il messaggio lo metti in un posto in cui non da fastidio, ma sia accessibile. L’importante è che non perdi il messaggio.
Fine.
Ogni altro tentativo di fare qualcosa di più sofisticato implicherebbe il noto problema dei due generali e l’unica possibilità di evitarlo sarebbe una simpatica transazione distribuita tra mittente, infrastruttura di messaggistica e destinatario. Cosa che ovviamente ci fa inorridire perché è uno dei motivi per cui abbiamo scelto un sistema di messaggistica.
In soldoni questo significa che da parte del mittente è impossibile sapere se il messaggio sia stato consegnato al destinatario. Non ci sono vie d’uscita, facciamocene una ragione.
Se osserviamo le due regole base di cui sopra, il massimo a cui possiamo aspirare è che o il messaggio è nella coda di destinazione, ma nulla ci può dire che sia stato processato correttamente, o nella peggiore delle ipotesi sta in una coda di errore, poison queue.
Questo comporta quindi che data una request che si aspetta una response non c’è nulla che garantisce che quella response sarà mai inviata, semplicemente perché non c’è nulla che garantisce la request sia mai andata dove speriamo che vada.
Vogliamo disaccoppiamento? Ci dobbiamo portare a casa anche le rogne del disaccoppiamento.
Questo principio fondamentale è quello che ci deve far sempre e solo dire: design for failure.
Nelle prossime puntate cercheremo di capire:
- Il perché tecnologico di quello che abbiamo detto guardando a come funzionano i due principali modelli di messaggistica: store & forward e broker
- Che cosa sono quindi pattern come request/* e pub/sub
- Che cosa vuol dire di conseguenza “design for failure”