node.js, mongodb, redis, sul degrado delle prestazioni di ubuntu in produzione, RAM libera, CPU 100%


11

Come suggerisce il titolo della domanda, sto facendo fatica a capire cosa può essere migliorato nella mia applicazione (o ottimizzato nel sistema operativo, Ubuntu) per ottenere prestazioni accettabili. Ma prima spiegherò l'architettura:

Il server front-end è una macchina a 8 core con 8 GB di RAM con Ubuntu 12.04. L'applicazione è scritta interamente in javascript ed eseguita in node.js v 0.8.22 (poiché alcuni moduli sembrano lamentarsi delle versioni più recenti del nodo) Uso nginx 1.4 per eseguire il proxy del traffico http dalla porta 80 e 443 agli operatori a 8 nodi gestiti e ha iniziato a utilizzare l'API del cluster di nodi. Uso l'ultima versione di socket.io 0.9.14 per gestire le connessioni socket web, su cui ho abilitato solo websocket e polling xhr come trasporti disponibili. Su questa macchina eseguo anche un'istanza di Redis (2.2)

Archivo dati persistenti (come utenti e punteggi) su un secondo server su mongodb (3.6) con 4 GB di RAM e 2 core.

L'app è in produzione da alcuni mesi (è stata eseguita su una singola scatola fino a poche settimane fa) ed è utilizzata da circa 18.000 utenti al giorno. Ha sempre funzionato molto bene a parte un problema principale: il degrado delle prestazioni. Con l'uso, la quantità di CPU utilizzata da ciascun processo aumenta fino a quando non staturizza il lavoratore (che non servirà più le richieste). L'ho risolto temporaneamente controllando la CPU in uso da ogni lavoratore ogni minuto e riavviandola se raggiunge il 98%. Quindi il problema qui è principalmente CPU, non RAM. La RAM non è più un problema da quando ho aggiornato socket.io 0.9.14 (la versione precedente perdeva memoria), quindi dubito che si tratti di un problema di perdita di memoria, soprattutto perché ora è la CPU che cresce abbastanza rapidamente ( Devo riavviare ogni lavoratore circa 10-12 volte al giorno!). La RAM in uso cresce anche a dire il vero, ma molto lentamente, 1 concerto ogni 2-3 giorni di utilizzo e la cosa strana è che non viene rilasciato anche quando riavvio completamente l'intera applicazione. Viene rilasciato solo se riavvio il server! questo non riesco davvero a capire ...

Ora ho scoperto nodefly che è sorprendente, quindi posso finalmente vedere cosa sta succedendo sul mio server di produzione e sto raccogliendo dati da un paio di giorni. Se qualcuno vuole vedere i grafici posso darti l'accesso, ma in fondo posso vedere che ho tra 80 e 200 connessioni simultanee! Mi aspettavo che node.js gestisse migliaia, non centinaia di richieste. Anche il tempo medio di risposta per il traffico http oscilla tra 500 e 1500 millisecondi, che penso sia davvero molto. Inoltre, proprio in questo momento con 1300 utenti online, questo è l'output di "ss -s":

Total: 5013 (kernel 5533)
TCP:   8047 (estab 4788, closed 3097, orphaned 139, synrecv 0, timewait 3097/0), ports 0

Transport Total     IP        IPv6
*         5533      -         -
RAW       0         0         0
UDP       0         0         0
TCP       4950      4948      2
INET      4950      4948      2
FRAG      0         0         0

il che dimostra che ho un sacco di connessioni chiuse in attesa del tempo. Ho aumentato il numero massimo di file aperti a 999999, ecco l'output di ulimit -a:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63724
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63724
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

Quindi ho pensato che il problema potesse riguardare il traffico http che per alcuni motivi satura le porte / socket disponibili (?), Ma una cosa non ha senso per me: perché quando riavvio i lavoratori e tutti i client si riconnettono in pochi secondi, il carico sulla cpu del lavoratore scende all'1% ed è in grado di soddisfare correttamente le richieste fino a quando non si satura dopo circa 1 ora (all'ora di punta)?

Sono principalmente un programmatore javascript, non un amministratore di sistema, quindi non so quanto carico dovrei aspettarmi di gestire con i miei server, ma sicuramente non funziona come dovrebbe. L'applicazione è stabile altrimenti e questo ultimo problema mi impedisce di spedire le versioni mobili dell'app che sono pronte, poiché ovviamente porteranno più carico e alla fine andranno in crash tutto!

Spero che ci sia qualcosa di ovvio che sto sbagliando, e qualcuno aiuterà a individuarlo ... sentiti libero di chiedermi ulteriori informazioni, e mi dispiace per la lunghezza della domanda, ma era necessario, credo ... Grazie in anticipo!


C'è un modo per ottenere qualcosa come il dump del thread da node.js? Probabilmente ci sono alcuni thread in un ciclo infinito. Inoltre, cosa sta effettivamente usando CPU? Cosa vedi topquando l'utilizzo della cpu è vicino al 100%?
camper

cpu è utilizzato interamente da nodejs, quando corro in alto vedo i processi del nodo che prendono tutta la cpu. Non sono sicuro di come posso generare un dump del thread dal nodo per essere onesti ...
Franjanko,

un'altra cosa da sottolineare è che la maggior parte del tempo della CPU sembra andare al sistema, non il tempo dell'utente
Franjanko,

Qualcuno sa almeno quante connessioni simultanee dovrei essere in grado di gestire con i server che ho installato? al momento supporto 200 connessioni simultanee max. Questo mi aiuterà a stimare quanto sono lontano da una configurazione ottimale ... grazie.
Franjanko,

Risposte:


10

Dopo alcuni giorni di intensi tentativi ed errori, sono felice di poter dire che ho capito dov'era il collo di bottiglia e lo pubblicherò qui in modo che altre persone possano trarre beneficio dalle mie scoperte.

Il problema risiede nelle connessioni pub / sub che stavo usando con socket.io, e in particolare nel RedisStore utilizzato da socket.io per gestire la comunicazione tra processi delle istanze socket.

Dopo aver capito che potevo implementare facilmente la mia versione di pub / sub usando redis, ho deciso di provarlo e ho rimosso redisStore da socket.io, lasciandolo con l'archivio di memoria predefinito (non ho bisogno di trasmettere a tutti i client connessi ma solo tra 2 utenti diversi collegati eventualmente su processi diversi)

Inizialmente ho dichiarato solo 2 connessioni redis globali x processo per la gestione del pub / sub su ogni client connesso e l'applicazione utilizzava meno risorse ma ero ancora influenzato da una crescita costante dell'utilizzo della CPU, quindi non era cambiato molto. Ma poi ho deciso di provare a creare 2 nuove connessioni da redis per ogni client per gestire il loro pub / sub solo nelle loro sessioni, quindi chiudere le connessioni una volta che l'utente si è disconnesso. Quindi dopo un giorno di utilizzo nella produzione, i CPU erano ancora allo 0-5% ... bingo! nessun processo si riavvia, nessun bug, con le prestazioni che mi aspettavo di avere. Ora posso dire che node.js oscilla e sono felice di averlo scelto per creare questa app.

Fortunatamente redis è stato progettato per gestire molte connessioni simultanee (diversamente da mongo) e per impostazione predefinita è impostato su 10k, che lascia spazio a circa 5k utenti simultanei, su una singola istanza redis, che è abbastanza per il momento per me, ma io ' ho letto che può essere spinto fino a 64k connessioni simultanee, quindi credo che questa architettura dovrebbe essere abbastanza solida.

A questo punto stavo pensando di implementare una sorta di pool di connessioni da ridisporre, per ottimizzarlo un po 'di più, ma non sono sicuro se ciò non causerà di nuovo l'accumulo di eventi / pub sulle connessioni, a meno che ciascuno di essi viene distrutto e ricreato ogni volta, per pulirli.

Comunque, grazie per le tue risposte, e sarò curioso di sapere cosa ne pensi e se hai altri suggerimenti.

Saluti.


2
Sto riscontrando quello che sembra essere lo stesso problema nella mia app di produzione, anche nuovo per il ruolo di amministratore del server. Seguo quello che hai fatto nel concetto, ma ho alcune domande su come farlo - forse potresti fornire un link ad alcune risorse nella tua risposta accettata? O semplicemente fornire ulteriori informazioni? In particolare su "Ma poi ho deciso di provare a creare 2 nuove connessioni da redis per ogni client per gestire il loro pub / sub solo nelle loro sessioni, quindi chiudere le connessioni una volta che l'utente si è disconnesso."
Toblerpwn,

2

Hai del codice sorgente da scaricare? Potrebbero essere connessioni al database non chiuse? Processi in attesa di connessioni HTTP che non si chiudono mai.

Puoi pubblicare alcuni registri?

Fai un ps -ef e assicurati che nulla sia ancora in esecuzione. Ho visto che i processi web lasciano zombi che non moriranno fino a quando non ucciderai -9. A volte l'arresto non funziona o non funziona completamente e quei thread o processi conterranno RAM e talvolta CPU.

Potrebbe essere un ciclo infinito da qualche parte nel codice o un processo bloccato che si blocca su una connessione db.

Quali moduli NPM stanno usando? Sono tutte le ultime?

Stai rilevando delle eccezioni? Vedi: http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/ Vedi: /programming/10122245/capture-node-js-crash-reason

Suggerimenti generali:

http://clock.co.uk/tech-blogs/preventing-http-raise-hangup-error-on-destroyed-socket-write-from-crashing-your-nodejs-server

http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever

http://hectorcorrea.com/blog/running-a-node-js-web-site-in-production-a-beginners-guide

/programming/1911015/how-to-debug-node-js-applications

https://github.com/dannycoates/node-inspector

http://elegantcode.com/2011/01/14/taking-baby-steps-with-node-js-debugging-with-node-inspector/


1

Non è una risposta in sé, poiché la tua domanda è più una favola che una domanda a risposta singola.

Solo per dire che ho costruito con successo un server node.js con socket.io che gestisce oltre 1 milione di connessioni persistenti con un payload di messaggi in media di 700 byte.

La scheda di interfaccia di rete a 1 Gbps era inizialmente satura e stavo vedendo MOLTO I / O in attesa degli eventi di pubblicazione su tutti i client.

Anche la rimozione di nginx dal ruolo proxy aveva restituito preziosa memoria, perché raggiungere un milione di connessioni persistenti con un solo server è un duro lavoro di ottimizzazione di configurazioni, applicazioni e ottimizzazione dei parametri del sistema operativo. Tieni presente che è fattibile solo con molta RAM (circa 1 milione di connessioni Web consuma circa 16 GB di RAM, con node.js, penso che l'uso di sock.js sarebbe l'ideale per un consumo di memoria insufficiente, ma per ora socket.io consuma così tanto).

Questo collegamento è stato il mio punto di partenza per raggiungere quel volume di connessioni con il nodo. Oltre ad essere un'app Erlang, tutta l'ottimizzazione del sistema operativo è praticamente indipendente dall'applicazione e dovrebbe essere utile a chiunque miri a molte connessioni persistenti (socket Web o polling lungo).

HTH,

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.