Come implementare una coda di messaggi su Redis?


29

Perché Redis per la coda?

Ho l'impressione che Redis possa diventare un buon candidato per l'implementazione di un sistema di accodamento. Fino a questo punto abbiamo utilizzato il nostro database MySQL con polling o RabbitMQ. Con RabbitMQ abbiamo avuto molti problemi: le librerie client sono molto povere e buggy e vorremmo non investire troppe ore degli sviluppatori per risolverle, alcuni problemi con la console di gestione del server, ecc. E, per il momento essendo almeno, non stiamo afferrando per millisecondi o spingendo seriamente le prestazioni, quindi fino a quando un sistema ha un'architettura che supporta una coda in modo intelligente, probabilmente siamo in buona forma.

Va bene, questo è lo sfondo. Fondamentalmente ho un modello di coda molto classico e semplice: diversi produttori che producono lavoro e molti consumatori consumano lavoro, e sia i produttori che i consumatori devono essere in grado di scalare in modo intelligente. Si scopre che un ingenuo PUBSUBnon funziona, dal momento che non voglio che tutti gli abbonati consumino lavoro, voglio solo che un abbonato riceva il lavoro. A prima vista, mi sembra che BRPOPLPUSHsia un design intelligente.

Possiamo usare BRPOPLPUSH?

La progettazione di base BRPOPLPUSHè che hai una coda di lavoro e una coda di avanzamento. Quando un consumatore riceve lavoro, inserisce atomicamente l'elemento nella coda di avanzamento e quando completa il lavoro, lo LREMè. Questo impedisce il blackholing del lavoro se i clienti muoiono e rende il monitoraggio abbastanza semplice - per esempio possiamo dire se c'è un problema che fa sì che i consumatori impieghino molto tempo a svolgere attività, oltre a dire se c'è un grande volume di attività.

Assicura

  • il lavoro viene consegnato esattamente a un consumatore
  • il lavoro finisce in una coda di avanzamento, quindi non può blackhole se un consumatore

Gli svantaggi

  • Mi sembra piuttosto strano che il miglior design che ho trovato non utilizzi in realtà PUBSUBpoiché questo sembra essere ciò su cui si concentrano la maggior parte dei post del blog sulla messa in coda su Redis. Quindi sento che mi manca qualcosa di ovvio. L'unico modo in cui vedo di usare PUBSUBdue volte senza consumare compiti è semplicemente di inviare una notifica che il lavoro è arrivato, che i consumatori possono quindi non bloccare RPOPLPUSH.
  • È impossibile richiedere più di un elemento di lavoro alla volta, il che sembra essere un problema di prestazioni. Non enorme per la nostra situazione, ma piuttosto dice chiaramente che questa operazione non è stata progettata per un alto rendimento o questa situazione
  • In breve: mi sto perdendo qualcosa di stupido?

Aggiungendo anche il tag node.js, perché è la lingua con cui mi occupo principalmente. Node può offrire alcune semplificazioni nell'implementazione, data la sua natura a thread singolo e non bloccante, ma inoltre sto usando la libreria node-redis e le soluzioni dovrebbero o possono essere sensibili anche ai suoi punti di forza e di debolezza.

Risposte:


5

Se vuoi usare Redis per una coda di messaggi in Node.js e non ti dispiace usare un modulo per questo, allora puoi provare RSMQ - Redis Simple Message Queue per Node. Non era disponibile al momento in cui è stata posta questa domanda, ma oggi è un'opzione praticabile.

Se vuoi implementare effettivamente la coda da solo come hai affermato nella tua domanda, potresti voler leggere la fonte di RSMQ perché sono solo 20 schermate di codice che fanno esattamente quello che stai chiedendo.

Vedere:


Accetterò questo a meno che non apprenda in seguito che è veramente difettoso o rotto o qualcosa del genere.
Djechlin,

22

Finora ho riscontrato alcune difficoltà. Vorrei documentare qui.

Come gestite la logica di riconnessione?

Questo è un problema difficile e un problema particolarmente difficile nella progettazione e implementazione di una coda di messaggi. I messaggi devono essere in grado di fare la fila da qualche parte quando i consumatori sono offline, quindi un semplice pub-sub non è abbastanza forte e i consumatori devono riconnettersi in uno stato di ascolto. I pop bloccati sono difficili da mantenere, poiché sono uno stato di ascolto non idempotente . L'ascolto dovrebbe essere un'operazione idempotente, tuttavia quando si ha a che fare con una disconnessione rispetto a un pop bloccante, si ha il piacere di pensare molto se la disconnessione è avvenuta subito dopo l'operazione riuscita o poco prima che l'operazione fallisse. Questo non è insormontabile, ma è indesiderabile.

Inoltre, l'operazione di ascolto dovrebbe essere il più semplice possibile. Idealmente dovrebbe avere queste proprietà:

  • L'ascolto è idempotente.
  • Il consumatore è sempre in ascolto e la logica di limitazione viene elaborata al di fuori del codice della logica di ascolto. RabbitMQ incapsula questo lasciando che il consumatore limiti il ​​numero di messaggi non compressi che può avere.
    In particolare, ho optato per un design scadente in cui il rientro in un pop bloccante dipendeva dal successo delle operazioni precedenti, che era fragile e richiedeva una riflessione approfondita.

Ora sto favorendo una soluzione Redis PUBSUB + RPOPLPUSH. Questo disaccoppia la notifica del lavoro dal consumo di lavoro, il che ci consente di escogitare una soluzione di ascolto pulita. PUBSUB è responsabile solo per la notifica del lavoro. La natura atomica di RPOPLPUSH è responsabile del consumo e delega del lavoro esattamente a un consumatore. All'inizio questa soluzione sembrava inutilmente complicata rispetto a un pop bloccante, ma ora vedo che la complicazione non era affatto inutile; stava risolvendo un problema difficile.

Tuttavia questa soluzione non è abbastanza banale:

  • i consumatori dovrebbero anche verificare la presenza di lavori sulla riconnessione.
  • i consumatori potrebbero voler fare comunque un sondaggio per nuovi lavori, per ridondanza. Se il sondaggio ha effettivamente successo, dovrebbe essere emesso un avviso, poiché ciò dovrebbe avvenire solo tra il consumo sul PUBSUB e il sondaggio su un RPOPLPUSH. Pertanto, molti risultati positivi del sondaggio indicano un sistema di abbonamento non funzionante.

Si noti che anche il design PUBSUB / RPOPLPUSH presenta problemi di ridimensionamento. Ogni consumatore riceve una leggera notifica di ogni messaggio, il che significa che questo ha un collo di bottiglia inutile. Ho il sospetto che sia possibile utilizzare i canali per frammentare il lavoro, ma questo è probabilmente un progetto complicato per funzionare bene.


Non sono sicuro di seguire il problema con il blocco dei consumatori. Mi sembra che se non c'è lavoro da elaborare i consumatori dovrebbero bloccare fino a quando non ce ne sono alcuni, anche se suppongo che il consumatore stia facendo anche altre cose che potrebbero essere una storia diversa, ma non è più un problema all'interno dell'applicazione e non tanto per la coda? Internet Explorer non bloccherebbe un thread all'interno di un'app più grande sarebbe una soluzione più elegante, in cui il thread sarebbe quindi in grado di notificare l'app quando ha recuperato un lavoro dalla coda. Forse è solo l'uso del nodo che sta creando la complicazione.
AaronM,

9
Sono curioso di sapere fino a che punto sei arrivato dallo scorso agosto. Sei stato in grado di risolvere i tuoi problemi con soddisfazione? Come li hai risolti?
AaronM,

3
AAA: proprio come @AaronM, mi piacerebbe sapere come sei progredito.
bjornl,

Concordato. Come è progredito? Mi piace l'idea di rimuovere RabbitMQ dallo stack e usare Redis che è lì comunque. Il mio problema è come registrare un consumatore usando RSMQ (nodo lib).
ra9r,

@raiglstorfer non ha lavorato lì per due anni: P sentiti libero di cercare e pubblicare ...
djechlin

0

Quindi la ragione principale per la scelta di usare RabbitMQ su Redis sono gli scenari di fallimento e il clustering.

Questo articolo lo spiega davvero meglio, quindi fornirò solo il link:

https://aphyr.com/posts/283-jepsen-redis

Redis sentinel e più recentemente il clustering redis non sono in grado di gestire una serie di scenari di errore molto basilari che lo hanno reso una scelta sbagliata per una coda.

RabbitMQ ha una sua serie di problemi, tuttavia, detto che è incredibilmente solido nella produzione ed è una buona coda di messaggi.

Ecco il post per il coniglio:

https://aphyr.com/posts/315-jepsen-rabbitmq

Quando guardi il teorico CAP (coerenza, disponibilità e gestione delle partizioni) puoi solo scegliere 2 di 3. Sfruttiamo RMQ per il CP (gestione della coerenza e delle partizioni) con il nostro carico di messaggi, se non siamo disponibili, non è ' la fine del mondo. Per non perdere i messaggi, usiamo ignora per la gestione delle partizioni per non perdere i messaggi. I duplicati possono essere gestiti poiché l'origine gestisce l'UUID.

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.