Perché database come coda così male? [chiuso]


33

Ho appena letto questo articolo e sono confuso.

Immaginiamo 1 webapp e 1 distinta applicazione che funge da "worker", entrambi condividendo lo stesso database .

Oh, ho detto "condivisione" .. ma di cosa parla l'articolo? :

In quarto luogo, condividere un database tra applicazioni (o servizi) è una cosa negativa. È troppo allettante per mettere lo stato amorfo condiviso lì e prima che tu lo sappia avrai un mostro enormemente accoppiato.

=> non sono d'accordo. Ci sono alcuni casi in cui applicazioni distinte fanno ancora parte della stessa unità e, pertanto, la nozione di "problema dell'accoppiamento" non ha senso in questo caso.

Continuiamo: la webapp gestisce le richieste HTTP del client e può aggiornare in qualsiasi momento alcuni aggregati (termine DDD), generando gli eventi di dominio corrispondenti.
L'obiettivo del lavoratore sarebbe quello di gestire quegli eventi di dominio elaborando i lavori necessari.

Il punto è:

Come devono essere trasmessi i dati sugli eventi al lavoratore?

La prima soluzione, come promuove l'articolo letto, sarebbe usare RabbitMQ, essendo un ottimo middleware orientato ai messaggi.

Il flusso di lavoro sarebbe semplice:

Ogni volta che il web dyno genera un evento, lo pubblica tramite RabbitMQ, che nutre il lavoratore.
Lo svantaggio sarebbe che nulla garantisce l' immediata coerenza tra il commit dell'aggiornamento aggregato e la pubblicazione dell'evento, senza affrontare i potenziali errori di invio ... o problemi hardware; questo è un altro problema principale.

Esempio: sarebbe possibile che un evento sia stato pubblicato senza successo dell'aggiornamento aggregato ... risultante in un evento che rappresenta una falsa rappresentazione del modello di dominio.
Si potrebbe sostenere che esiste un XA globale (commit in due fasi), ma non è una soluzione che si adatta a tutti i database o middleware.

Quindi quale potrebbe essere una buona soluzione per garantire questa coerenza immediata? :
IMO, memorizzando l'evento nel database, nella stessa transazione locale dell'aggiornamento aggregato.
Un semplice programmatore asincrono verrebbe creato e responsabile di interrogare gli eventi non pubblicati correnti dal database e inviarli a RabbitMQ, che a sua volta popola il lavoratore.

Ma perché è necessario un programmatore aggiuntivo sul lato webapp e comunque: perché in questo caso è necessario RabbitMQ?

Con questa soluzione, sembra logicamente che RabbitMQ potrebbe non essere necessario, soprattutto perché il database è condiviso.
In effetti, in ogni caso, abbiamo visto che la coerenza immediata comporta un polling dal database.
Quindi, perché il lavoratore non dovrebbe essere direttamente responsabile di questo sondaggio?

Pertanto, mi chiedo perché così tanti articoli sul web criticano difficilmente l'accodamento dei database, promuovendo nel contempo il middleware orientato ai messaggi.

Estratto dell'articolo:

Semplice, usa lo strumento giusto per il lavoro: questo scenario richiede un sistema di messaggistica. Risolve tutti i problemi sopra descritti; niente più polling, consegna efficiente dei messaggi, nessuna necessità di cancellare i messaggi completati dalle code e nessuno stato condiviso.

E consistenza immediata, ignorata?

Per riassumere, sembra davvero che qualunque sia il caso, ovvero database condiviso o meno, abbiamo bisogno del polling del database .

Ho perso alcune nozioni critiche?

Grazie


2
Il polling è una specie di aringa rossa, perché quasi tutti i principali database hanno un meccanismo per notificare in modo asincrono qualche altro processo che è tempo di estrarre un po 'di lavoro da una tabella.
Blrfl

Risposte:


28

Se stai costruendo una semplice applicazione a basso traffico, c'è qualcosa da dire su come mantenere un altro componente fuori dal tuo sistema. È molto probabile che non usare un bus di messaggi sia la risposta giusta per te. Tuttavia, suggerirei di costruire il tuo sistema in modo da poter sostituire il sistema di code basato su database con una soluzione middleware. Sono d'accordo con l'articolo. Un database non è lo strumento giusto per il sistema basato sulla coda, ma può essere abbastanza buono per te.

I sistemi basati su code come RabbitMq sono costruiti su larga scala su hardware moderato. La loro architettura è in grado di raggiungere questo obiettivo evitando processi che rallentano per loro natura il sistema di database conforme ACID . Poiché un bus di messaggi deve solo garantire che un messaggio sia archiviato ed elaborato correttamente, non è necessario preoccuparsi di bloccare e scrivere i registri delle transazioni. Entrambi questi concetti sono assolutamente necessari per un sistema ACID, ma spesso sono causa di contesa.

Per quanto riguarda le prestazioni, si tratta di: hai una tabella SQL. Molte letture e molte scritture. Entrambi richiedono una sorta di blocco per aggiornare righe, pagine e indici. Il tuo meccanismo di polling blocca costantemente un indice per effettuare ricerche su di esso. Questo impedisce che si verifichino le scritture; nella migliore delle ipotesi sono in coda. Il codice che sta eseguendo l'elaborazione si blocca anche per aggiornare lo stato sulla coda mentre vengono completati o non riescono. Sì, è possibile eseguire l'ottimizzazione delle query dopo l'ottimizzazione per farlo funzionare oppure è possibile utilizzare un sistema appositamente progettato per il carico di lavoro richiesto. Un RabbitMq consuma questo tipo di carico di lavoro senza nemmeno sudare; Inoltre, puoi salvare il tuo database dal carico di lavoro dandogli più spazio per scalare facendo altre cose.

Un'altra cosa da considerare è che la maggior parte dei sistemi di coda in genere non utilizza una tecnica di polling (alcuni consentono HTTP, ma raccomandano di evitare di utilizzarli per il lato di ricezione). RabbitMq utilizza protocolli di rete appositamente progettati per bus di messaggi come AMPQ .

Modifica: aggiunta del caso d'uso.

Il modo in cui ho usato Rabbit è che ho avuto un endpoint API che accetta una modifica che richiede una tabella di database molto utilizzata. Questa tabella è in costante contesa e, a volte, non sarà in grado di salvare una modifica in modo tempestivo dall'API. Quello che faccio invece è scrivere la richiesta di modifica in una coda e quindi disporre di un servizio che gestisca questi messaggi come sono in grado. Se si verifica una contesa con il database, la coda aumenta semplicemente e l'elaborazione dei messaggi viene ritardata. In genere il tempo di elaborazione scende nell'intervallo di 14 ms, ma in tempi di forte contesa si arriva a 2-3 secondi.


Come hai potuto gestire la coerenza immediata in questo caso? Se la pubblicazione viene effettuata, ma subito dopo, la transazione responsabile dell'aggiornamento dei rollback del modello di dominio ... Il middleware sarebbe totalmente inconsapevole e elaborerebbe l'evento.
Mik378

Hai scritto: "non ha bisogno di preoccuparsi del blocco". Ma c'è sicuramente una sorta di blocco per garantire l'ordine crescente (nel tempo) di eventi indirizzati (verso il lavoratore), no?
Mik378

@ Mik378 Dai un'occhiata a questo articolo sull'idempotenza del messaggio . Sì, tecnicamente perdi una certa promessa di coerenza, ma scommetto che troverai quello che guadagni in termini di affidabilità dei tempi di attività delle applicazioni e delle prestazioni ne vale la pena. È anche abbastanza facile cambiare il modo in cui elaborate i messaggi per rendere le perdite abbastanza indolori.
brianfeucht,

2
Sì, è necessario il blocco per garantire l'ordine. Alcuni sistemi di coda possono fornire questo a prezzo di prestazione. Se riesci ad accettare il fatto che a volte le operazioni avvengono in modo anomalo e trovi un modo per gestirlo dal lato del processore, otterrai un vantaggio esponenziale dal punto di vista delle prestazioni.
brianfeucht,

1
@ Mik378 - Ho aggiunto un caso d'uso alla mia risposta. Spero possa essere d'aiuto!
brianfeucht,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.