Stiamo facendo la coda e la serializzazione correttamente?

13

Elaboriamo i messaggi attraverso una varietà di servizi (un messaggio toccherà probabilmente 9 servizi prima che sia fatto, ognuno facendo una specifica funzione relativa all'IO). Al momento abbiamo una combinazione del caso peggiore (serializzazione del contratto dati XML) e del caso migliore (MSMQ in memoria) per le prestazioni.

La natura del messaggio indica che i nostri dati serializzati terminano a circa 12-15 kilobyte e elaboriamo circa 4 milioni di messaggi a settimana. I messaggi persistenti in MSMQ erano troppo lenti per noi e, man mano che i dati crescono, sentiamo la pressione dei file mappati in memoria di MSMQ. Il server ha un consumo di memoria di 16 GB ed è in crescita, solo per l'accodamento. Anche le prestazioni si verificano quando l'utilizzo della memoria è elevato, poiché la macchina inizia lo scambio. Stiamo già eseguendo il comportamento di auto-pulizia di MSMQ.

Mi sento come se ci fosse una parte che stiamo sbagliando qui. Ho provato a utilizzare RavenDB per mantenere i messaggi e ad accodare un identificatore, ma le prestazioni erano molto lente (1000 messaggi al minuto, nel migliore dei casi). Non sono sicuro che sia il risultato dell'uso della versione di sviluppo o di cosa, ma abbiamo sicuramente bisogno di un throughput più elevato [1]. Il concetto ha funzionato molto bene in teoria, ma le prestazioni non erano all'altezza.

Il modello di utilizzo ha un servizio che funge da router, che viene letto da tutti. Gli altri servizi collegheranno le informazioni in base al loro hook di terze parti e inoltreranno di nuovo al router. La maggior parte degli oggetti viene toccata 9-12 volte, sebbene circa il 10% sia costretto ad aggirarsi in questo sistema per un po 'finché le terze parti non rispondono in modo appropriato. I servizi in questo momento spiegano questo e hanno comportamenti di sonno appropriati, poiché utilizziamo il campo prioritario del messaggio per questo motivo.

Quindi, la mia domanda, è qual è lo stack ideale per il trasferimento di messaggi tra macchine discrete-ma-LAN in un ambiente C # / Windows? Normalmente inizierei con BinaryFormatter invece della serializzazione XML , ma questa è una tana del coniglio se un modo migliore è quello di scaricare la serializzazione in un archivio di documenti. Quindi, la mia domanda.

[1]: La natura della nostra attività significa che prima elaboriamo i messaggi, più soldi guadagniamo. Abbiamo dimostrato empiricamente che elaborare un messaggio più tardi nella settimana significa che siamo meno propensi a fare quei soldi. Mentre le prestazioni di "1000 al minuto" sembrano molto veloci, abbiamo davvero bisogno di un numero che superi i 10.000 / minuto. Solo perché sto dando i numeri nei messaggi a settimana non significa che abbiamo un'intera settimana per elaborare quei messaggi.

=============== modifica:

Informazioni aggiuntive

In base ai commenti, aggiungerò qualche chiarimento:

  • Non sono sicuro che la serializzazione sia il nostro collo di bottiglia. Ho eseguito il benchmark dell'applicazione e mentre la serializzazione si presenta nel grafico del calore, è responsabile solo del 2,5-3% circa dell'utilizzo della CPU del servizio.

  • Sono principalmente preoccupato per la permanenza dei nostri messaggi e il potenziale uso improprio di MSMQ. Utilizziamo messaggi non transazionali e non persistenti, in modo che possiamo mantenere attive le code e mi piacerebbe avere almeno dei messaggi persistenti per sopravvivere al riavvio.

  • L'aggiunta di più RAM è una misura in sospeso. La macchina è già passata da 4 GB - > 16 GB di RAM e diventa sempre più difficile rimuoverlo per continuare ad aggiungerne altri.

  • A causa del pattern di instradamento a stella dell'applicazione, metà del tempo in cui un oggetto viene scoppiato e poi spinto in una coda, non cambia affatto. Questo si presta di nuovo (IMO) per archiviarlo in qualche tipo di archivio di valori-chiave altrove e semplicemente per passare gli identificatori di messaggi.

  • Il pattern di instradamento stellare è parte integrante dell'applicazione e non cambierà. Non possiamo applicarlo come metodo perché ogni pezzo lungo la strada opera in modo asincrono (in modalità sondaggio) e vogliamo centralizzare il comportamento dei tentativi in un unico punto.

  • La logica dell'applicazione è scritta in C #, gli oggetti sono POCO immutabili, l'ambiente di distribuzione di destinazione è Windows Server 2012 e siamo autorizzati a installare macchine aggiuntive se un particolare software è supportato solo in Linux.

  • I miei obiettivi sono il mantenimento del throughput corrente riducendo l'ingombro della memoria e aumentando la tolleranza ai guasti con un esborso minimo di capitale.

posta Bryan Boettcher 15.10.2013 - 21:21
fonte

3 risposte

1

Ecco alcuni parametri di riferimento della coda che potrebbero interessarti. MSMQ dovrebbe essere in grado di gestire messaggi 10K al secondo. Potrebbe essere un problema di configurazione o forse i clienti non stanno tenendo il passo con la lettura della coda? Nota inoltre che ZeroMQ è incredibilmente veloce in questi benchmark (circa 100K messaggi al secondo), non offre un'opzione di persistenza, ma dovrebbe portarti dove vuoi essere performante.

    
risposta data 25.10.2013 - 19:39
fonte
4

Abbiamo avuto una situazione in qualche modo simile diversi anni fa, con un sistema di messaggi in coda (impronte digitali nel nostro caso). Abbiamo strongmente valutato la persistenza dei pacchetti di dati accodati, ma abbiamo scoperto che accodare tutto su disco e consumare la coda dal disco era molto costoso.

Se passassimo alle code basate sulla memoria, le prestazioni erano eccezionali, ma avevamo un grosso problema. Di tanto in tanto, i consumatori delle code non sono più disponibili per molto tempo (gli elementi del consumatore e del produttore nel nostro caso sono collegati tramite WAN), quindi la coda del produttore crescerebbe fino a renderla ingestibile e come il tuo caso, una volta che il consumo di memoria era molto alto, l'eccessivo consumo di memoria durante lo swap ha portato il sistema a una scansione completa.

Abbiamo progettato una coda che abbiamo battezzato VMQueue (per Virtual Memory Queue, un pessimo nome in retrospettiva). L'idea di questa coda è che se il processo del consumatore è in esecuzione fino alla pari, in altre parole, l'elaborazione è abbastanza veloce da essere in grado di mantenere il numero di elementi in coda al di sotto di un certo livello, quindi ha sostanzialmente le stesse prestazioni di una coda basata sulla memoria. Tuttavia, quando il consumatore rallenta o diventa non disponibile e la coda del produttore cresce fino a una determinata dimensione, la coda inizierà automaticamente gli elementi di paging sul e dal disco (usando la serializzazione di BinaryFormatter tra l'altro). Questo processo mantiene l'utilizzo della memoria completamente controllato e il processo di paging è veloce, o almeno molto più veloce dello scambio di memoria virtuale che si verifica durante il carico di memoria pesante. Una volta che il consumatore riesce a drenare la coda sotto la soglia, riprende a funzionare come una coda di memoria pura

Se il sistema si arresta in modo anomalo o si riavvia, la coda è in grado di recuperare tutti gli elementi pagati che sono stati memorizzati sul disco, ma perderà solo gli elementi che erano ancora conservati in memoria prima del crash. Se puoi permetterti di perdere un numero limitato di pacchetti durante un arresto anomalo o riavviare, questa coda potrebbe essere utile.

Se sei interessato, posso condividere il codice sorgente di VMQueue in modo che tu possa giocarci. La coda accetterà qualsiasi classe contrassegnata come serializzabile. Al momento della creazione della coda si stabilisce la dimensione della pagina in numero di elementi. L'interfaccia di classe è praticamente la stessa di una classe di coda standard. Tuttavia, il codice è molto vecchio (.net 1.1), quindi purtroppo non esiste un'interfaccia generica.

So che passare dalla collaudata tecnologia MSMQ è una grande scommessa, tuttavia questa coda ha funzionato in modo affidabile per quasi 6 anni e ci ha permesso di sopravvivere e riprenderci dagli scenari in cui la macchina del produttore è offline da diverse settimane! Per favore fammi sapere se sei interessato. :)

    
risposta data 25.10.2013 - 01:07
fonte
1

Il sistema HP ProLiant ML350G5 riceve 82k transazioni al minuto, ovvero ha più di 8 volte quel throughput "10k / minuto" che hai citato.

Performance: 82,774 tpmC

Inoltre, ad essere onesti, sarei andato con 64 o addirittura 128 GB di RAM - la RAM è economica. Greenspun sottolinea la differenza tra "lanciare RAM su di esso" e "ottenere un ragazzo intelligente istruito dal MIT per ottimizzarlo", e la RAM vince.

He ended up with a SQL Server machine equipped with 64 GB of RAM and a handful of front-end machines running ASP.NET pages... The site, swaptree.com, handles its current membership of more than 400,000 users (growing rapidly) without difficulty...

Notare che "la macchina è già passata a 16 GB di RAM" non è abbastanza, con un articolo che indicava un server che gestiva 400k utenti su 64 GB di RAM.

    
risposta data 25.10.2013 - 15:34
fonte

Leggi altre domande sui tag