L'approccio comune, come già menzionato Ozz , è una coda di messaggi . Dal punto di vista del design, una coda di messaggi è essenzialmente una coda FIFO , che è un tipo di dati piuttosto fondamentale:
Ciò che rende speciale una coda di messaggi è che mentre l'applicazione è responsabile della messa in coda, un processo diverso sarebbe responsabile della messa in coda. Nel gergo di accodamento, l'applicazione è il mittente dei messaggi e il processo di decodifica è il destinatario. L'ovvio vantaggio è che l'intero processo è asincrono, il destinatario funziona indipendentemente dal mittente, purché ci siano messaggi da elaborare. L'ovvio svantaggio è che hai bisogno di un componente aggiuntivo, il mittente, affinché tutto funzioni.
Poiché ora la tua architettura si basa su due componenti che scambiano messaggi, puoi usare il termine fantasia comunicazione tra processi .
In che modo l'introduzione di una coda influisce sul design dell'applicazione?
Alcune azioni nella tua applicazione generano email. L'introduzione di una coda di messaggi significherebbe che tali azioni dovrebbero ora inviare i messaggi alla coda (e nient'altro). Quei messaggi dovrebbero contenere la quantità minima assoluta di informazioni necessarie per costruire le e-mail quando il destinatario può elaborarle.
Formato e contenuto dei messaggi
Il formato e il contenuto dei tuoi messaggi dipende completamente da te, ma dovresti tenere a mente quanto più piccolo è il migliore. La tua coda dovrebbe essere la più veloce su cui scrivere ed elaborare il più possibile, lanciando una grande quantità di dati probabilmente creerà un collo di bottiglia.
Inoltre, diversi servizi di accodamento basati su cloud hanno restrizioni sulle dimensioni dei messaggi e possono dividere i messaggi più grandi. Non ti accorgerai, i messaggi divisi verranno serviti come uno quando li chiedi, ma ti verranno addebitati più messaggi (supponendo ovviamente che tu stia utilizzando un servizio che richiede una tariffa).
Design del ricevitore
Dato che stiamo parlando di un'applicazione web, un approccio comune per il tuo ricevitore sarebbe un semplice cron script. Funzionerebbe ogni x
minuto (o secondi) e dovrebbe:
- Pop
n
quantità di messaggi dalla coda,
- Elaborare i messaggi (ovvero inviare le e-mail).
Nota che sto dicendo pop invece di get o fetch, perché il tuo destinatario non sta semplicemente recuperando gli elementi dalla coda, ma li sta anche cancellando (cioè rimuovendoli dalla coda o contrassegnandoli come elaborati). Come esattamente ciò accadrà dipende dall'implementazione della coda dei messaggi e dalle esigenze specifiche dell'applicazione.
Naturalmente quello che sto descrivendo è essenzialmente un'operazione batch , il modo più semplice di elaborare una coda. A seconda delle esigenze, potresti voler elaborare i messaggi in modo più complicato (ciò richiederebbe anche una coda più complicata).
Traffico
Il destinatario potrebbe prendere in considerazione il traffico e regolare il numero di messaggi che elabora in base al traffico nel momento in cui viene eseguito. Un approccio semplicistico sarebbe quello di prevedere le tue elevate ore di traffico in base ai dati sul traffico passato e supponendo che tu sia andato con uno script cron che viene eseguito ogni x
minuto, potresti fare qualcosa del genere:
if(
now() > 2pm && now() < 7pm
) {
process(10);
} else {
process(100);
}
function process(count) {
for(i=0; i<=count; i++) {
message = dequeue();
mail(message)
}
}
Un approccio molto ingenuo e sporco, ma funziona. In caso contrario, l'altro approccio sarebbe quello di scoprire il traffico corrente del tuo server ad ogni iterazione e adattare di conseguenza il numero di elementi del processo. Per favore non ottimizzare il micro se non è assolutamente necessario, sprecheresti il tuo tempo.
Memoria della coda
Se l'applicazione utilizza già un database, una sola tabella su di essa sarebbe la soluzione più semplice:
CREATE TABLE message_queue (
id int(11) NOT NULL AUTO_INCREMENT,
timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed enum('0','1') NOT NULL DEFAULT '0',
message varchar(255) NOT NULL,
PRIMARY KEY (id),
KEY timestamp (timestamp),
KEY processed (processed)
)
Non è davvero più complicato di così. Ovviamente puoi renderlo complicato di cui hai bisogno, ad esempio puoi aggiungere un campo prioritario (il che significherebbe che questa non è più una coda FIFO, ma se davvero ne hai bisogno, chi se ne frega?). Potresti anche renderlo più semplice, saltando il campo elaborato (ma poi dovresti eliminare le righe dopo averle elaborate).
Una tabella di database sarebbe l'ideale per 2000 messaggi al giorno, ma probabilmente non si ridimensionerebbe bene per milioni di messaggi al giorno. Ci sono un milione di fattori da considerare, tutto nella tua infrastruttura gioca un ruolo nella scalabilità generale della tua applicazione.
In ogni caso, supponendo che tu abbia già identificato la coda basata su database come un collo di bottiglia, il passo successivo sarebbe quello di esaminare un servizio basato su cloud. Amazon SQS è l'unico servizio che ho usato e fatto quello che promette. Sono sicuro che ci sono alcuni servizi simili là fuori.
Anche le code basate sulla memoria sono qualcosa da considerare, specialmente per le code di breve durata. memcached è eccellente come memoria per la coda dei messaggi.
Qualunque sia lo spazio di archiviazione su cui decidi di costruire la tua coda, sii intelligente e astratto. Né il mittente né il destinatario devono essere collegati a una memoria specifica, altrimenti passare a una memoria diversa in un secondo momento sarebbe una PITA completa.
Approccio alla vita reale
Ho creato una coda di messaggi per e-mail molto simile a quella che stai facendo. Era su un progetto PHP e l'ho costruito attorno a Zend Queue , un componente di Zend Framework che offre diversi adattatori per diversi archivi. I miei depositi dove:
- Array PHP per test unitari,
- Amazon SQS in produzione,
- MySQL su sviluppatori e ambienti di test.
I miei messaggi erano semplici come potevano essere, la mia applicazione ha creato piccoli array con le informazioni essenziali ( [user_id, reason]
). L'archivio messaggi era una versione serializzata di quell'array (prima era il formato di serializzazione interno di PHP, poi JSON, non ricordo perché ho cambiato). La reason
è una costante e, naturalmente, ho un grande tavolo da qualche parte che le mappe reason
di spiegazioni più complete (sono riuscito a trasmettere circa 500 email ai clienti con la criptica reason
invece del messaggio più piena una volta).
Ulteriori letture
standard:
Utensili:
Letture interessanti: